[evolution] Bug 540362: [webkit-composer] Use webkit for composer



commit 8650fb139a9143f04615de74ff569bce3e0c4ce3
Author: Tomas Popela <tpopela redhat com>
Date:   Mon Jun 9 16:32:25 2014 +0200

    Bug 540362: [webkit-composer] Use webkit for composer
    
    Merge wip/webkit-composer branch into master.

 addressbook/gui/contact-editor/Makefile.am         |    6 +-
 addressbook/gui/contact-list-editor/Makefile.am    |    6 +-
 addressbook/gui/widgets/Makefile.am                |    8 +-
 addressbook/gui/widgets/eab-contact-formatter.c    |    8 +-
 addressbook/importers/Makefile.am                  |    8 +-
 addressbook/printing/Makefile.am                   |    6 +-
 addressbook/util/Makefile.am                       |    8 +-
 art/Makefile.am                                    |   12 +-
 art/confidential-stamp.jpg                         |  Bin 0 -> 5708 bytes
 art/draft-paper.png                                |  Bin 0 -> 597 bytes
 art/draft-stamp.jpg                                |  Bin 0 -> 4660 bytes
 art/midnight-stars.jpg                             |  Bin 0 -> 7190 bytes
 art/paper.png                                      |  Bin 0 -> 266 bytes
 art/rect.png                                       |  Bin 0 -> 3795 bytes
 art/ribbon.jpg                                     |  Bin 0 -> 3193 bytes
 art/texture.png                                    |  Bin 0 -> 137 bytes
 calendar/alarm-notify/Makefile.am                  |    8 +-
 calendar/gui/Makefile.am                           |    8 +-
 calendar/gui/dialogs/Makefile.am                   |    6 +-
 calendar/importers/Makefile.am                     |    6 +-
 composer/Makefile.am                               |    8 +-
 composer/e-composer-actions.c                      |   81 +-
 composer/e-composer-actions.h                      |    4 +-
 composer/e-composer-activity.c                     |  187 -
 composer/e-composer-activity.h                     |   64 -
 composer/e-composer-private.c                      |  858 ++-
 composer/e-composer-private.h                      |   25 +-
 composer/e-composer-spell-header.c                 |   13 -
 composer/e-composer-spell-header.h                 |    3 -
 composer/e-msg-composer.c                          | 1318 ++---
 composer/e-msg-composer.h                          |   30 +-
 configure.ac                                       |   58 +-
 data/evolution.convert                             |    1 -
 data/org.gnome.evolution.mail.gschema.xml.in       |   15 +-
 doc/reference/evolution-mail-composer/Makefile.am  |    2 -
 .../evolution-mail-composer-docs.sgml              |    1 -
 .../evolution-mail-composer-sections.txt           |   19 -
 .../evolution-mail-composer.types                  |    2 -
 doc/reference/evolution-mail-formatter/Makefile.am |    2 -
 doc/reference/evolution-shell/Makefile.am          |    2 -
 doc/reference/evolution-util/Makefile.am           |    5 +-
 .../evolution-util/evolution-util-docs.sgml        |   30 +-
 .../evolution-util/evolution-util-sections.txt     | 1081 ++++-
 doc/reference/evolution-util/evolution-util.types  |   26 +-
 e-util/Makefile.am                                 |   67 +-
 e-util/e-action-combo-box.c                        |   42 +-
 e-util/e-action-combo-box.h                        |    1 +
 e-util/e-color-chooser-widget.c                    |  253 +
 e-util/e-color-chooser-widget.h                    |   71 +
 e-util/e-color-combo.c                             |  976 +++
 e-util/e-color-combo.h                             |   96 +
 e-util/e-emoticon-action.c                         |  278 +
 e-util/e-emoticon-action.h                         |   73 +
 e-util/e-emoticon-chooser-menu.c                   |  184 +
 e-util/e-emoticon-chooser-menu.h                   |   70 +
 e-util/e-emoticon-chooser.c                        |  178 +
 e-util/e-emoticon-chooser.h                        |   77 +
 e-util/e-emoticon-tool-button.c                    |  695 +++
 e-util/e-emoticon-tool-button.h                    |   75 +
 e-util/e-emoticon.c                                |  118 +
 e-util/e-emoticon.h                                |   53 +
 e-util/e-focus-tracker.c                           |   45 +
 e-util/e-html-editor-actions.c                     | 2028 +++++++
 e-util/e-html-editor-actions.h                     |  155 +
 e-util/e-html-editor-cell-dialog.c                 |  872 +++
 e-util/e-html-editor-cell-dialog.h                 |   72 +
 e-util/e-html-editor-dialog.c                      |  248 +
 e-util/e-html-editor-dialog.h                      |   74 +
 e-util/e-html-editor-find-dialog.c                 |  224 +
 e-util/e-html-editor-find-dialog.h                 |   73 +
 e-util/e-html-editor-hrule-dialog.c                |  421 ++
 e-util/e-html-editor-hrule-dialog.h                |   70 +
 e-util/e-html-editor-image-dialog.c                |  703 +++
 e-util/e-html-editor-image-dialog.h                |   73 +
 e-util/e-html-editor-link-dialog.c                 |  390 ++
 e-util/e-html-editor-link-dialog.h                 |   70 +
 e-util/e-html-editor-manager.ui                    |  181 +
 e-util/e-html-editor-page-dialog.c                 |  513 ++
 e-util/e-html-editor-page-dialog.h                 |   70 +
 e-util/e-html-editor-paragraph-dialog.c            |  154 +
 e-util/e-html-editor-paragraph-dialog.h            |   71 +
 e-util/e-html-editor-private.h                     |  103 +
 e-util/e-html-editor-replace-dialog.c              |  288 +
 e-util/e-html-editor-replace-dialog.h              |   71 +
 e-util/e-html-editor-selection.c                   | 5576 +++++++++++++++++
 e-util/e-html-editor-selection.h                   |  250 +
 e-util/e-html-editor-spell-check-dialog.c          |  710 +++
 e-util/e-html-editor-spell-check-dialog.h          |   73 +
 e-util/e-html-editor-table-dialog.c                |  866 +++
 e-util/e-html-editor-table-dialog.h                |   69 +
 e-util/e-html-editor-text-dialog.c                 |  298 +
 e-util/e-html-editor-text-dialog.h                 |   69 +
 e-util/e-html-editor-utils.c                       |  116 +
 e-util/e-html-editor-utils.h                       |   44 +
 e-util/e-html-editor-view.c                        | 6303 ++++++++++++++++++++
 e-util/e-html-editor-view.h                        |  164 +
 e-util/e-html-editor.c                             | 1178 ++++
 e-util/e-html-editor.h                             |  108 +
 e-util/e-image-chooser-dialog.c                    |  223 +
 e-util/e-image-chooser-dialog.h                    |   74 +
 e-util/e-mail-signature-editor.c                   |  319 +-
 e-util/e-mail-signature-editor.h                   |    8 +-
 e-util/e-mail-signature-manager.c                  |   18 +-
 e-util/e-mail-signature-preview.c                  |    8 +-
 e-util/e-misc-utils.c                              |   44 +
 e-util/e-misc-utils.h                              |    3 +
 e-util/e-name-selector-entry.c                     |    6 +-
 e-util/e-spell-checker.c                           |  783 +++
 e-util/e-spell-checker.h                           |   95 +
 e-util/e-spell-dictionary.c                        |  797 +++
 e-util/e-spell-dictionary.h                        |   99 +
 e-util/e-spell-entry.c                             |  373 +-
 e-util/e-spell-entry.h                             |    7 +-
 e-util/e-util-enums.h                              |  221 +
 e-util/e-util.h                                    |   26 +-
 e-util/e-web-view-gtkhtml.c                        | 2352 --------
 e-util/e-web-view-gtkhtml.h                        |  209 -
 e-util/e-web-view.c                                |  150 +-
 e-util/e-web-view.h                                |   13 +
 e-util/test-html-editor.c                          |  497 ++
 em-format/Makefile.am                              |    8 +-
 em-format/e-mail-formatter-enums.h                 |   18 -
 em-format/e-mail-formatter-quote-attachment.c      |    9 +-
 em-format/e-mail-formatter-quote-text-html.c       |    2 +-
 em-format/e-mail-formatter-quote-text-plain.c      |    4 +-
 em-format/e-mail-formatter-quote.c                 |   54 +-
 em-format/e-mail-formatter.c                       |   10 +-
 em-format/e-mail-formatter.h                       |    4 +-
 libemail-engine/Makefile.am                        |    2 -
 mail/Makefile.am                                   |    5 +-
 mail/e-http-request.c                              |   72 +-
 mail/e-mail-display.c                              |  130 +-
 mail/e-mail-reader-utils.c                         |    1 -
 mail/e-mail-reader.c                               |    2 +-
 mail/em-composer-utils.c                           |  120 +-
 mail/em-utils.c                                    |    5 +
 mail/em-utils.h                                    |    1 +
 mail/importers/Makefile.am                         |    6 +-
 mail/mail-config.ui                                |   93 +-
 modules/addressbook/Makefile.am                    |    8 +-
 modules/backup-restore/Makefile.am                 |    4 -
 modules/backup-restore/evolution-backup-tool.c     |   44 +-
 modules/book-config-google/Makefile.am             |    6 +-
 modules/book-config-ldap/Makefile.am               |    8 +-
 modules/book-config-local/Makefile.am              |    6 +-
 modules/book-config-webdav/Makefile.am             |    6 +-
 modules/cal-config-caldav/Makefile.am              |    8 +-
 modules/cal-config-contacts/Makefile.am            |    6 +-
 modules/cal-config-google/Makefile.am              |    8 +-
 modules/cal-config-local/Makefile.am               |    6 +-
 modules/cal-config-weather/Makefile.am             |    8 +-
 modules/cal-config-webcal/Makefile.am              |    6 +-
 modules/calendar/Makefile.am                       |    6 +-
 modules/composer-autosave/Makefile.am              |   41 +-
 modules/composer-autosave/e-composer-autosave.c    |   14 +-
 modules/contact-photos/Makefile.am                 |    2 -
 modules/gravatar/Makefile.am                       |    2 -
 modules/itip-formatter/Makefile.am                 |    6 +-
 modules/itip-formatter/itip-view.c                 |   16 +-
 modules/itip-formatter/plugin/Makefile.am          |    6 +-
 modules/mail-config/Makefile.am                    |    2 -
 modules/mail/Makefile.am                           |    6 +-
 modules/mail/e-mail-shell-backend.c                |   19 +-
 modules/mail/e-mail-shell-view-private.c           |    3 +-
 modules/mail/e-mail-shell-view-private.h           |    1 -
 modules/mail/em-composer-prefs.c                   |  118 +-
 modules/mail/em-composer-prefs.h                   |    5 +-
 modules/mail/em-mailer-prefs.c                     |   13 +-
 modules/mailto-handler/Makefile.am                 |    6 +-
 modules/mdn/Makefile.am                            |    6 +-
 modules/offline-alert/Makefile.am                  |    6 +-
 modules/plugin-lib/Makefile.am                     |    6 +-
 modules/plugin-manager/Makefile.am                 |    6 +-
 modules/prefer-plain/Makefile.am                   |    6 +-
 modules/prefer-plain/plugin/Makefile.am            |    6 +-
 modules/settings/Makefile.am                       |    6 +-
 modules/settings/e-settings-deprecated.c           |    2 +-
 modules/settings/e-settings-spell-checker.c        |  117 +
 modules/settings/e-settings-spell-checker.h        |   65 +
 modules/settings/e-settings-web-view-gtkhtml.c     |  324 -
 modules/settings/e-settings-web-view-gtkhtml.h     |   64 -
 modules/settings/e-settings-web-view.c             |  189 +-
 modules/settings/evolution-module-settings.c       |    4 +-
 modules/spamassassin/Makefile.am                   |    6 +-
 modules/startup-wizard/Makefile.am                 |    2 -
 modules/text-highlight/Makefile.am                 |    6 +-
 modules/tnef-attachment/Makefile.am                |    8 +-
 modules/vcard-inline/Makefile.am                   |    4 +-
 modules/web-inspector/Makefile.am                  |    6 +-
 plugins/attachment-reminder/Makefile.am            |    6 +-
 plugins/bbdb/Makefile.am                           |    6 +-
 plugins/dbx-import/Makefile.am                     |    6 +-
 plugins/email-custom-header/Makefile.am            |    6 +-
 plugins/email-custom-header/email-custom-header.c  |   11 +-
 plugins/external-editor/Makefile.am                |    6 +-
 plugins/external-editor/external-editor.c          |  106 +-
 plugins/face/Makefile.am                           |    6 +-
 plugins/face/face.c                                |   12 +-
 plugins/mail-notification/Makefile.am              |    6 +-
 plugins/mail-to-task/Makefile.am                   |    6 +-
 plugins/mailing-list-actions/Makefile.am           |    6 +-
 plugins/pst-import/Makefile.am                     |    8 +-
 plugins/publish-calendar/Makefile.am               |    8 +-
 plugins/save-calendar/Makefile.am                  |    6 +-
 plugins/templates/Makefile.am                      |    6 +-
 plugins/templates/templates.c                      |    6 +-
 po/POTFILES.in                                     |   19 +-
 po/POTFILES.skip                                   |    1 +
 shell/Makefile.am                                  |    8 +-
 smime/gui/Makefile.am                              |    8 +-
 smime/lib/Makefile.am                              |    8 +-
 211 files changed, 32916 insertions(+), 5470 deletions(-)
---
diff --git a/addressbook/gui/contact-editor/Makefile.am b/addressbook/gui/contact-editor/Makefile.am
index e62b6cf..8356a7e 100644
--- a/addressbook/gui/contact-editor/Makefile.am
+++ b/addressbook/gui/contact-editor/Makefile.am
@@ -11,8 +11,8 @@ libecontacteditor_la_CPPFLAGS =                               \
        -DG_LOG_DOMAIN=\"contact-editor\"               \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 libecontacteditor_la_SOURCES =                         \
        eab-editor.c                            \
@@ -36,7 +36,7 @@ libecontacteditor_la_LIBADD =                                         \
        $(EVOLUTION_ADDRESSBOOK_LIBS)                                   \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 ui_DATA =                              \
        contact-editor.ui               \
diff --git a/addressbook/gui/contact-list-editor/Makefile.am b/addressbook/gui/contact-list-editor/Makefile.am
index faf2d43..c1c2bd2 100644
--- a/addressbook/gui/contact-list-editor/Makefile.am
+++ b/addressbook/gui/contact-list-editor/Makefile.am
@@ -11,8 +11,8 @@ libecontactlisteditor_la_CPPFLAGS =                   \
        -DG_LOG_DOMAIN=\"contact-list-editor\"          \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 libecontactlisteditor_la_SOURCES =             \
        e-contact-list-editor.c                 \
@@ -29,7 +29,7 @@ libecontactlisteditor_la_LIBADD =                             \
        $(top_builddir)/shell/libevolution-shell.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 ui_DATA  = contact-list-editor.ui
 
diff --git a/addressbook/gui/widgets/Makefile.am b/addressbook/gui/widgets/Makefile.am
index 715f84b..26b8b01 100644
--- a/addressbook/gui/widgets/Makefile.am
+++ b/addressbook/gui/widgets/Makefile.am
@@ -18,10 +18,10 @@ libeabwidgets_la_CPPFLAGS =                         \
        -I$(top_builddir)/shell                         \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
        $(CHAMPLAIN_CFLAGS)                             \
        $(GEO_CFLAGS)                                   \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 eabincludedir = $(privincludedir)/addressbook/gui/widgets
 
@@ -81,9 +81,9 @@ libeabwidgets_la_LIBADD =                                     \
        $(top_builddir)/addressbook/util/libeabutil.la          \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
        $(CHAMPLAIN_LIBS)                                       \
-       $(GEO_LIBS)
+       $(GEO_LIBS)                                             \
+       $(NULL)
 
 dist-hook:
        cd $(distdir); rm -f $(BUILT_SOURCES)
diff --git a/addressbook/gui/widgets/eab-contact-formatter.c b/addressbook/gui/widgets/eab-contact-formatter.c
index 7bfa468..34b3a5e 100644
--- a/addressbook/gui/widgets/eab-contact-formatter.c
+++ b/addressbook/gui/widgets/eab-contact-formatter.c
@@ -1395,13 +1395,7 @@ collapse_contacts_list (WebKitDOMEventTarget *event_target,
        gboolean hidden;
 
        document = user_data;
-#if WEBKIT_CHECK_VERSION(2,2,0)  /* XXX should really be (2,1,something) */
-       id = webkit_dom_element_get_id (
-               WEBKIT_DOM_ELEMENT (event_target));
-#else
-       id = webkit_dom_html_element_get_id (
-               WEBKIT_DOM_HTML_ELEMENT (event_target));
-#endif
+       id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (event_target));
 
        list_id = g_strconcat ("list-", id, NULL);
        list = webkit_dom_document_get_element_by_id (document, list_id);
diff --git a/addressbook/importers/Makefile.am b/addressbook/importers/Makefile.am
index 89f8f9b..0d1d262 100644
--- a/addressbook/importers/Makefile.am
+++ b/addressbook/importers/Makefile.am
@@ -10,8 +10,8 @@ libevolution_addressbook_importers_la_CPPFLAGS =      \
        -I$(top_builddir)/addressbook                   \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 libevolution_addressbook_importers_la_SOURCES = \
        evolution-ldif-importer.c               \
@@ -27,7 +27,7 @@ libevolution_addressbook_importers_la_LIBADD = \
        $(top_builddir)/addressbook/util/libeabutil.la                  \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)                                                 \
-       $(IMPORTERS_LIBS)
+       $(IMPORTERS_LIBS)                                               \
+       $(NULL)
 
 -include $(top_srcdir)/git.mk
diff --git a/addressbook/printing/Makefile.am b/addressbook/printing/Makefile.am
index a6e3aac..c077359 100644
--- a/addressbook/printing/Makefile.am
+++ b/addressbook/printing/Makefile.am
@@ -13,8 +13,8 @@ libecontactprint_la_CPPFLAGS =                        \
        -DEVOLUTION_ECPSDIR=\""$(ecpsdir)"\"    \
        $(EVOLUTION_DATA_SERVER_CFLAGS)         \
        $(GNOME_PLATFORM_CFLAGS)                \
-       $(GTKHTML_CFLAGS)                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                 \
+       $(NULL)
 
 noinst_LTLIBRARIES = libecontactprint.la
 
@@ -30,7 +30,7 @@ libecontactprint_la_LIBADD =                                  \
        $(top_builddir)/addressbook/util/libeabutil.la          \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 EXTRA_DIST =           \
        $(ecps_DATA)
diff --git a/addressbook/util/Makefile.am b/addressbook/util/Makefile.am
index 4657dbb..33f1f1b 100644
--- a/addressbook/util/Makefile.am
+++ b/addressbook/util/Makefile.am
@@ -12,9 +12,9 @@ libeabutil_la_CPPFLAGS =                              \
        -I$(top_srcdir)/shell                           \
        $(CAMEL_CFLAGS)                                 \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
-       $(GTKHTML_CFLAGS)                               \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 libeabutil_la_SOURCES =                                        \
        eab-book-util.c                                 \
@@ -27,8 +27,8 @@ libeabutil_la_LIBADD =                                        \
        $(top_builddir)/shell/libevolution-shell.la     \
        $(CAMEL_LIBS)                                   \
        $(EVOLUTION_DATA_SERVER_LIBS)                   \
-       $(GTKHTML_LIBS)                                 \
-       $(GNOME_PLATFORM_LIBS)
+       $(GNOME_PLATFORM_LIBS)                          \
+       $(NULL)
 
 dist-hook:
        cd $(distdir); rm -f $(BUILT_SOURCES)
diff --git a/art/Makefile.am b/art/Makefile.am
index 850be7e..276d435 100644
--- a/art/Makefile.am
+++ b/art/Makefile.am
@@ -1,7 +1,15 @@
 images_DATA = \
-       world_map-960.png               \
+       confidential-stamp.jpg          \
+       draft-paper.png                 \
+       draft-stamp.jpg                 \
+       midnight-stars.jpg              \
+       minus.png                       \
+       paper.png                       \
        plus.png                        \
-       minus.png
+       rect.png                        \
+       ribbon.jpg                      \
+       texture.png                     \
+       world_map-960.png
 
 EXTRA_DIST =                           \
        README                          \
diff --git a/art/confidential-stamp.jpg b/art/confidential-stamp.jpg
new file mode 100644
index 0000000..0dece7c
Binary files /dev/null and b/art/confidential-stamp.jpg differ
diff --git a/art/draft-paper.png b/art/draft-paper.png
new file mode 100644
index 0000000..177d568
Binary files /dev/null and b/art/draft-paper.png differ
diff --git a/art/draft-stamp.jpg b/art/draft-stamp.jpg
new file mode 100644
index 0000000..623f50e
Binary files /dev/null and b/art/draft-stamp.jpg differ
diff --git a/art/midnight-stars.jpg b/art/midnight-stars.jpg
new file mode 100644
index 0000000..22f01b3
Binary files /dev/null and b/art/midnight-stars.jpg differ
diff --git a/art/paper.png b/art/paper.png
new file mode 100644
index 0000000..bca355b
Binary files /dev/null and b/art/paper.png differ
diff --git a/art/rect.png b/art/rect.png
new file mode 100644
index 0000000..b7e633c
Binary files /dev/null and b/art/rect.png differ
diff --git a/art/ribbon.jpg b/art/ribbon.jpg
new file mode 100644
index 0000000..03ca65e
Binary files /dev/null and b/art/ribbon.jpg differ
diff --git a/art/texture.png b/art/texture.png
new file mode 100644
index 0000000..b0925a6
Binary files /dev/null and b/art/texture.png differ
diff --git a/calendar/alarm-notify/Makefile.am b/calendar/alarm-notify/Makefile.am
index 057ac6a..ecd24af 100644
--- a/calendar/alarm-notify/Makefile.am
+++ b/calendar/alarm-notify/Makefile.am
@@ -21,8 +21,8 @@ evolution_alarm_notify_CPPFLAGS =                     \
        $(GNOME_PLATFORM_CFLAGS)                        \
        $(LIBNOTIFY_CFLAGS)                             \
        $(CANBERRA_CFLAGS)                              \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 ui_DATA =              \
        alarm-notify.ui
@@ -55,8 +55,8 @@ evolution_alarm_notify_LDADD =                                                \
        $(GNOME_PLATFORM_LIBS)                                          \
        $(LIBNOTIFY_LIBS)                                               \
        $(CANBERRA_LIBS)                                                \
-       $(GTKHTML_LIBS)
-       $(EVOLUTIONALARMNOTIFYICON)
+       $(EVOLUTIONALARMNOTIFYICON)                                     \
+       $(NULL)
 
 evolution_alarm_notify_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
 
diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am
index de752c1..2dca7d5 100644
--- a/calendar/gui/Makefile.am
+++ b/calendar/gui/Makefile.am
@@ -67,9 +67,9 @@ libevolution_calendar_la_CPPFLAGS =                   \
        -DPREFIX=\""$(prefix)"\"                        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
        $(LIBSOUP_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 etspec_DATA =                          \
        e-calendar-table.etspec         \
@@ -207,8 +207,8 @@ libevolution_calendar_la_LIBADD =                                   \
        $(top_builddir)/e-util/libevolution-util.la                     \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)                                                 \
-       $(LIBSOUP_LIBS)
+       $(LIBSOUP_LIBS)                                                 \
+       $(NULL)
 
 libevolution_calendar_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
 
diff --git a/calendar/gui/dialogs/Makefile.am b/calendar/gui/dialogs/Makefile.am
index 9c7b305..ec5afaa 100644
--- a/calendar/gui/dialogs/Makefile.am
+++ b/calendar/gui/dialogs/Makefile.am
@@ -14,8 +14,8 @@ libcal_dialogs_la_CPPFLAGS =                          \
        -DPREFIX=\""$(prefix)"\"                        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 ecalendarincludedir = $(privincludedir)/calendar/gui/dialogs
 
@@ -52,7 +52,7 @@ libcal_dialogs_la_LIBADD =                                    \
        $(top_builddir)/addressbook/util/libeabutil.la          \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 libcal_dialogs_la_SOURCES =            \
        alarm-dialog.c                  \
diff --git a/calendar/importers/Makefile.am b/calendar/importers/Makefile.am
index 206a852..957de01 100644
--- a/calendar/importers/Makefile.am
+++ b/calendar/importers/Makefile.am
@@ -9,8 +9,8 @@ libevolution_calendar_importers_la_CPPFLAGS =           \
        -I$(top_builddir)/calendar                      \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 libevolution_calendar_importers_la_SOURCES = \
        evolution-calendar-importer.h        \
@@ -23,6 +23,6 @@ libevolution_calendar_importers_la_LIBADD =                   \
        $(top_builddir)/shell/libevolution-shell.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 -include $(top_srcdir)/git.mk
diff --git a/composer/Makefile.am b/composer/Makefile.am
index aa78864..86a411a 100644
--- a/composer/Makefile.am
+++ b/composer/Makefile.am
@@ -10,7 +10,6 @@ evolution_mail_composer_includedir = $(privincludedir)/composer
 
 evolution_mail_composer_include_HEADERS =      \
        e-composer-actions.h                    \
-       e-composer-activity.h                   \
        e-composer-common.h                     \
        e-composer-from-header.h                \
        e-composer-header-table.h               \
@@ -36,13 +35,12 @@ libevolution_mail_composer_la_CPPFLAGS =                            \
        -DG_LOG_DOMAIN=\"composer\"                                     \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                                 \
        $(GNOME_PLATFORM_CFLAGS)                                        \
-       $(GTKHTML_CFLAGS)                                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                         \
+       $(NULL)
 
 libevolution_mail_composer_la_SOURCES =                \
        $(evolution_mail_composer_include_HEADERS) \
        e-composer-actions.c                    \
-       e-composer-activity.c                   \
        e-composer-from-header.c                \
        e-composer-header-table.c               \
        e-composer-header.c                     \
@@ -63,7 +61,7 @@ libevolution_mail_composer_la_LIBADD =                        \
        $(top_builddir)/addressbook/gui/contact-list-editor/libecontactlisteditor.la    \
        $(EVOLUTION_DATA_SERVER_LIBS)                   \
        $(GNOME_PLATFORM_LIBS)                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 ui_DATA = evolution-composer.ui
 
diff --git a/composer/e-composer-actions.c b/composer/e-composer-actions.c
index 047bf73..f917a2c 100644
--- a/composer/e-composer-actions.c
+++ b/composer/e-composer-actions.c
@@ -134,20 +134,24 @@ static void
 action_pgp_encrypt_cb (GtkToggleAction *action,
                        EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
-       editor = GTKHTML_EDITOR (composer);
-       gtkhtml_editor_set_changed (editor, TRUE);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_set_changed (view, TRUE);
 }
 
 static void
 action_pgp_sign_cb (GtkToggleAction *action,
                     EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
-       editor = GTKHTML_EDITOR (composer);
-       gtkhtml_editor_set_changed (editor, TRUE);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_set_changed (view, TRUE);
 }
 
 static void
@@ -198,12 +202,14 @@ static void
 action_save_cb (GtkAction *action,
                 EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor = GTKHTML_EDITOR (composer);
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        const gchar *filename;
        gint fd;
        GError *error = NULL;
 
-       filename = gtkhtml_editor_get_filename (editor);
+       editor = e_msg_composer_get_editor (composer);
+       filename = e_html_editor_get_filename (editor);
        if (filename == NULL) {
                gtk_action_activate (ACTION (SAVE_AS));
                return;
@@ -233,7 +239,7 @@ action_save_cb (GtkAction *action,
        } else
                close (fd);
 
-       if (!gtkhtml_editor_save (editor, filename, TRUE, &error)) {
+       if (!e_html_editor_save (editor, filename, TRUE, &error)) {
                e_alert_submit (
                        E_ALERT_SINK (composer),
                        E_ALERT_NO_SAVE_FILE,
@@ -242,13 +248,15 @@ action_save_cb (GtkAction *action,
                return;
        }
 
-       gtkhtml_editor_run_command (GTKHTML_EDITOR (composer), "saved");
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_set_changed (view, TRUE);
 }
 
 static void
 action_save_as_cb (GtkAction *action,
                    EMsgComposer *composer)
 {
+       EHTMLEditor *editor;
        GtkWidget *dialog;
        gchar *filename;
        gint response;
@@ -272,8 +280,9 @@ action_save_as_cb (GtkAction *action,
        if (response != GTK_RESPONSE_OK)
                goto exit;
 
+       editor = e_msg_composer_get_editor (composer);
        filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
-       gtkhtml_editor_set_filename (GTKHTML_EDITOR (composer), filename);
+       e_html_editor_set_filename (editor, filename);
        g_free (filename);
 
        gtk_action_activate (ACTION (SAVE));
@@ -300,20 +309,24 @@ static void
 action_smime_encrypt_cb (GtkToggleAction *action,
                          EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
-       editor = GTKHTML_EDITOR (composer);
-       gtkhtml_editor_set_changed (editor, TRUE);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_set_changed (view, TRUE);
 }
 
 static void
 action_smime_sign_cb (GtkToggleAction *action,
                       EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
-       editor = GTKHTML_EDITOR (composer);
-       gtkhtml_editor_set_changed (editor, TRUE);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_set_changed (view, TRUE);
 }
 
 static gboolean
@@ -514,15 +527,15 @@ e_composer_actions_init (EMsgComposer *composer)
        GtkActionGroup *action_group;
        GtkAccelGroup *accel_group;
        GtkUIManager *ui_manager;
-       GtkhtmlEditor *editor;
-       EWebViewGtkHTML *web_view;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        gboolean visible;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
-       editor = GTKHTML_EDITOR (composer);
-       web_view = e_msg_composer_get_web_view (composer);
-       ui_manager = gtkhtml_editor_get_ui_manager (editor);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       ui_manager = e_html_editor_get_ui_manager (editor);
 
        /* Composer Actions */
        action_group = composer->priv->composer_actions;
@@ -566,23 +579,33 @@ e_composer_actions_init (EMsgComposer *composer)
                ACTION (SAVE_DRAFT), "short-label", _("Save Draft"), NULL);
 
        g_object_bind_property (
-               composer, "html-mode",
+               view, "html-mode",
                ACTION (PICTURE_GALLERY), "sensitive",
                G_BINDING_SYNC_CREATE);
 
        g_object_bind_property (
-               web_view, "editable",
-               GTKHTML_EDITOR_ACTION_EDIT_MENU (editor), "sensitive",
+               view, "editable",
+               e_html_editor_get_action (editor, "edit-menu"), "sensitive",
                G_BINDING_SYNC_CREATE);
 
        g_object_bind_property (
-               web_view, "editable",
-               GTKHTML_EDITOR_ACTION_FORMAT_MENU (editor), "sensitive",
+               view, "editable",
+               e_html_editor_get_action (editor, "format-menu"), "sensitive",
                G_BINDING_SYNC_CREATE);
 
        g_object_bind_property (
-               web_view, "editable",
-               GTKHTML_EDITOR_ACTION_INSERT_MENU (editor), "sensitive",
+               view, "editable",
+               e_html_editor_get_action (editor, "insert-menu"), "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       g_object_bind_property (
+               view, "editable",
+               e_html_editor_get_action (editor, "options-menu"), "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       g_object_bind_property (
+               view, "editable",
+               e_html_editor_get_action (editor, "picture-gallery"), "sensitive",
                G_BINDING_SYNC_CREATE);
 
 #if defined (HAVE_NSS)
diff --git a/composer/e-composer-actions.h b/composer/e-composer-actions.h
index 611154e..2919531 100644
--- a/composer/e-composer-actions.h
+++ b/composer/e-composer-actions.h
@@ -18,7 +18,9 @@
 #define E_COMPOSER_ACTIONS_H
 
 #define E_COMPOSER_ACTION(composer, name) \
-       (gtkhtml_editor_get_action (GTKHTML_EDITOR (composer), (name)))
+       (e_html_editor_get_action ( \
+               e_msg_composer_get_editor ( \
+               E_MSG_COMPOSER (composer)), (name)))
 
 #define E_COMPOSER_ACTION_ATTACH(composer) \
        E_COMPOSER_ACTION ((composer), "attach")
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index 59e1625..bc3b6d8 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -27,15 +27,19 @@
 /* Initial height of the picture gallery. */
 #define GALLERY_INITIAL_HEIGHT 150
 
+#define UNICODE_ZERO_WIDTH_SPACE "\xe2\x80\x8b"
+
 static void
 composer_setup_charset_menu (EMsgComposer *composer)
 {
+       EHTMLEditor *editor;
        GtkUIManager *ui_manager;
        const gchar *path;
        GList *list;
        guint merge_id;
 
-       ui_manager = gtkhtml_editor_get_ui_manager (GTKHTML_EDITOR (composer));
+       editor = e_msg_composer_get_editor (composer);
+       ui_manager = e_html_editor_get_ui_manager (editor);
        path = "/main-menu/options-menu/charset-menu";
        merge_id = gtk_ui_manager_new_merge_id (ui_manager);
 
@@ -58,62 +62,22 @@ composer_setup_charset_menu (EMsgComposer *composer)
 }
 
 static void
-msg_composer_url_requested_cb (GtkHTML *html,
-                               const gchar *uri,
-                               GtkHTMLStream *stream,
-                               EMsgComposer *composer)
-{
-       GByteArray *array;
-       GHashTable *hash_table;
-       CamelDataWrapper *wrapper;
-       CamelStream *camel_stream;
-       CamelMimePart *mime_part;
-
-       hash_table = composer->priv->inline_images_by_url;
-       mime_part = g_hash_table_lookup (hash_table, uri);
-
-       if (mime_part == NULL) {
-               hash_table = composer->priv->inline_images;
-               mime_part = g_hash_table_lookup (hash_table, uri);
-       }
-
-       /* If this is not an inline image request,
-        * allow the signal emission to continue. */
-       if (mime_part == NULL)
-               return;
-
-       array = g_byte_array_new ();
-       camel_stream = camel_stream_mem_new_with_byte_array (array);
-       wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
-       camel_data_wrapper_decode_to_stream_sync (
-               wrapper, camel_stream, NULL, NULL);
-
-       gtk_html_write (html, stream, (gchar *) array->data, array->len);
-
-       gtk_html_end (html, stream, GTK_HTML_STREAM_OK);
-
-       g_object_unref (camel_stream);
-
-       /* gtk_html_end() destroys the GtkHTMLStream, so we need to
-        * stop the signal emission so nothing else tries to use it. */
-       g_signal_stop_emission_by_name (html, "url-requested");
-}
-
-static void
 composer_update_gallery_visibility (EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        GtkToggleAction *toggle_action;
        gboolean gallery_active;
-       gboolean html_mode;
+       gboolean is_html;
 
-       editor = GTKHTML_EDITOR (composer);
-       html_mode = gtkhtml_editor_get_html_mode (editor);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       is_html = e_html_editor_view_get_html_mode (view);
 
        toggle_action = GTK_TOGGLE_ACTION (ACTION (PICTURE_GALLERY));
        gallery_active = gtk_toggle_action_get_active (toggle_action);
 
-       if (html_mode && gallery_active) {
+       if (is_html && gallery_active) {
                gtk_widget_show (composer->priv->gallery_scrolled_window);
                gtk_widget_show (composer->priv->gallery_icon_view);
        } else {
@@ -122,30 +86,16 @@ composer_update_gallery_visibility (EMsgComposer *composer)
        }
 }
 
-static void
-composer_spell_languages_changed (EMsgComposer *composer,
-                                  GList *languages)
-{
-       EComposerHeader *header;
-       EComposerHeaderTable *table;
-
-       table = e_msg_composer_get_header_table (composer);
-       header = e_composer_header_table_get_header (
-               table, E_COMPOSER_HEADER_SUBJECT);
-
-       e_composer_spell_header_set_languages (
-               E_COMPOSER_SPELL_HEADER (header), languages);
-}
-
 void
 e_composer_private_constructed (EMsgComposer *composer)
 {
        EMsgComposerPrivate *priv = composer->priv;
        EFocusTracker *focus_tracker;
+       EComposerHeader *header;
        EShell *shell;
-       EWebViewGtkHTML *web_view;
        EClientCache *client_cache;
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        GtkUIManager *ui_manager;
        GtkAction *action;
        GtkWidget *container;
@@ -158,14 +108,14 @@ e_composer_private_constructed (EMsgComposer *composer)
        gint ii;
        GError *error = NULL;
 
-       editor = GTKHTML_EDITOR (composer);
-       ui_manager = gtkhtml_editor_get_ui_manager (editor);
+       editor = e_msg_composer_get_editor (composer);
+       ui_manager = e_html_editor_get_ui_manager (editor);
+       view = e_html_editor_get_view (editor);
 
        settings = g_settings_new ("org.gnome.evolution.mail");
 
        shell = e_msg_composer_get_shell (composer);
        client_cache = e_shell_get_client_cache (shell);
-       web_view = e_msg_composer_get_web_view (composer);
 
        /* Each composer window gets its own window group. */
        window = GTK_WINDOW (composer);
@@ -179,19 +129,17 @@ e_composer_private_constructed (EMsgComposer *composer)
        priv->extra_hdr_names = g_ptr_array_new ();
        priv->extra_hdr_values = g_ptr_array_new ();
 
-       priv->inline_images = g_hash_table_new_full (
-               g_str_hash, g_str_equal,
-               (GDestroyNotify) g_free,
-               (GDestroyNotify) NULL);
-
-       priv->inline_images_by_url = g_hash_table_new_full (
-               g_str_hash, g_str_equal,
-               (GDestroyNotify) g_free,
-               (GDestroyNotify) g_object_unref);
-
        priv->charset = e_composer_get_default_charset ();
 
+       priv->is_from_draft = FALSE;
        priv->is_from_message = FALSE;
+       priv->is_from_new_message = FALSE;
+       priv->set_signature_from_message = FALSE;
+       priv->disable_signature = FALSE;
+       priv->busy = FALSE;
+       priv->saved_editable= FALSE;
+
+       priv->focused_entry = NULL;
 
        e_composer_actions_init (composer);
 
@@ -216,48 +164,58 @@ e_composer_private_constructed (EMsgComposer *composer)
 
        focus_tracker = e_focus_tracker_new (GTK_WINDOW (composer));
 
-       action = gtkhtml_editor_get_action (editor, "cut");
+       action = e_html_editor_get_action (editor, "cut");
        e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
 
-       action = gtkhtml_editor_get_action (editor, "copy");
+       action = e_html_editor_get_action (editor, "copy");
        e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
 
-       action = gtkhtml_editor_get_action (editor, "paste");
+       action = e_html_editor_get_action (editor, "paste");
        e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
 
-       action = gtkhtml_editor_get_action (editor, "select-all");
+       action = e_html_editor_get_action (editor, "select-all");
        e_focus_tracker_set_select_all_action (focus_tracker, action);
 
        priv->focus_tracker = focus_tracker;
 
-       container = editor->vbox;
+       widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       gtk_container_add (GTK_CONTAINER (composer), widget);
+       gtk_widget_show (widget);
+
+       container = widget;
 
-       /* Construct the activity bar. */
+       /* Construct the main menu and toolbar. */
 
-       widget = e_activity_bar_new ();
+       widget = e_html_editor_get_managed_widget (editor, "/main-menu");
        gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
-       priv->activity_bar = g_object_ref_sink (widget);
-       /* EActivityBar controls its own visibility. */
-
-       /* Construct the alert bar for errors. */
+       gtk_widget_show (widget);
 
-       widget = e_alert_bar_new ();
+       widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
        gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
-       priv->alert_bar = g_object_ref_sink (widget);
-       /* EAlertBar controls its own visibility. */
+       gtk_widget_show (widget);
 
        /* Construct the header table. */
 
        widget = e_composer_header_table_new (client_cache);
        gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
        gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
-       gtk_box_reorder_child (GTK_BOX (container), widget, 2);
-       priv->header_table = g_object_ref_sink (widget);
+       priv->header_table = g_object_ref (widget);
        gtk_widget_show (widget);
 
-       g_signal_connect (
-               G_OBJECT (composer), "spell-languages-changed",
-               G_CALLBACK (composer_spell_languages_changed), NULL);
+       header = e_composer_header_table_get_header (
+               E_COMPOSER_HEADER_TABLE (widget),
+               E_COMPOSER_HEADER_SUBJECT);
+       g_object_bind_property (
+               view, "spell-checker",
+               header->input_widget, "spell-checker",
+               G_BINDING_SYNC_CREATE);
+
+       /* Construct the editing toolbars.  We'll have to reparent
+        * the embedded EHTMLEditorView a little further down. */
+
+       widget = GTK_WIDGET (editor);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
 
        /* Construct the attachment paned. */
 
@@ -267,8 +225,8 @@ e_composer_private_constructed (EMsgComposer *composer)
        gtk_widget_show (widget);
 
        g_object_bind_property (
-               web_view, "editable",
-               widget, "editable",
+               view, "editable",
+               widget, "sensitive",
                G_BINDING_SYNC_CREATE);
 
        container = e_attachment_paned_get_content_area (
@@ -288,13 +246,13 @@ e_composer_private_constructed (EMsgComposer *composer)
                GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
        gtk_widget_set_size_request (widget, -1, GALLERY_INITIAL_HEIGHT);
        gtk_paned_pack1 (GTK_PANED (container), widget, FALSE, FALSE);
-       priv->gallery_scrolled_window = g_object_ref_sink (widget);
+       priv->gallery_scrolled_window = g_object_ref (widget);
        gtk_widget_show (widget);
 
-       /* Reparent the scrolled window containing the GtkHTML widget
-        * into the content area of the top attachment pane. */
+       /* Reparent the scrolled window containing the web view
+        * widget into the content area of the top attachment pane. */
 
-       widget = GTK_WIDGET (web_view);
+       widget = GTK_WIDGET (view);
        widget = gtk_widget_get_parent (widget);
        gtk_widget_reparent (widget, container);
 
@@ -310,16 +268,16 @@ e_composer_private_constructed (EMsgComposer *composer)
        priv->gallery_icon_view = g_object_ref_sink (widget);
        g_free (gallery_path);
 
-       e_signal_connect_notify (
-               composer, "notify::html-mode",
-               G_CALLBACK (composer_update_gallery_visibility), NULL);
+       e_signal_connect_notify_swapped (
+               view, "notify::mode",
+               G_CALLBACK (composer_update_gallery_visibility), composer);
 
        g_signal_connect_swapped (
                ACTION (PICTURE_GALLERY), "toggled",
                G_CALLBACK (composer_update_gallery_visibility), composer);
 
-       /* XXX What is this for? */
-       g_object_set_data (G_OBJECT (composer), "vbox", editor->vbox);
+       /* Initial sync */
+       composer_update_gallery_visibility (composer);
 
        /* Bind headers to their corresponding actions. */
 
@@ -361,20 +319,21 @@ e_composer_private_constructed (EMsgComposer *composer)
                        G_BINDING_SYNC_CREATE);
        }
 
-       /* Install a handler for inline images. */
+       /* Disable actions that start asynchronous activities while an
+        * asynchronous activity is in progress. We enforce this with
+        * a simple inverted binding to EMsgComposer's "busy" property. */
 
-       /* XXX We no longer use GtkhtmlEditor::uri-requested because it
-        *     conflicts with EWebView's url_requested() method, which
-        *     unconditionally launches an async operation.  I changed
-        *     GtkHTML::url-requested to be a G_SIGNAL_RUN_LAST so that
-        *     our handler runs first.  If we can handle the request
-        *     we'll stop the signal emission to prevent EWebView from
-        *     launching an async operation.  Messy, but works until we
-        *     switch to WebKit.  --mbarnes */
+       g_object_bind_property (
+               composer, "busy",
+               priv->async_actions, "sensitive",
+               G_BINDING_SYNC_CREATE |
+               G_BINDING_INVERT_BOOLEAN);
 
-       g_signal_connect (
-               web_view, "url-requested",
-               G_CALLBACK (msg_composer_url_requested_cb), composer);
+       g_object_bind_property (
+               composer, "busy",
+               priv->header_table, "sensitive",
+               G_BINDING_SYNC_CREATE |
+               G_BINDING_INVERT_BOOLEAN);
 
        g_object_unref (settings);
 }
@@ -389,21 +348,16 @@ e_composer_private_dispose (EMsgComposer *composer)
                composer->priv->shell = NULL;
        }
 
+       if (composer->priv->editor != NULL) {
+               g_object_unref (composer->priv->editor);
+               composer->priv->editor = NULL;
+       }
+
        if (composer->priv->header_table != NULL) {
                g_object_unref (composer->priv->header_table);
                composer->priv->header_table = NULL;
        }
 
-       if (composer->priv->activity_bar != NULL) {
-               g_object_unref (composer->priv->activity_bar);
-               composer->priv->activity_bar = NULL;
-       }
-
-       if (composer->priv->alert_bar != NULL) {
-               g_object_unref (composer->priv->alert_bar);
-               composer->priv->alert_bar = NULL;
-       }
-
        if (composer->priv->attachment_paned != NULL) {
                g_object_unref (composer->priv->attachment_paned);
                composer->priv->attachment_paned = NULL;
@@ -434,11 +388,10 @@ e_composer_private_dispose (EMsgComposer *composer)
                composer->priv->composer_actions = NULL;
        }
 
-       g_clear_object (&composer->priv->gallery_icon_view);
-       g_clear_object (&composer->priv->gallery_scrolled_window);
-
-       g_hash_table_remove_all (composer->priv->inline_images);
-       g_hash_table_remove_all (composer->priv->inline_images_by_url);
+       if (composer->priv->gallery_scrolled_window != NULL) {
+               g_object_unref (composer->priv->gallery_scrolled_window);
+               composer->priv->gallery_scrolled_window = NULL;
+       }
 
        if (composer->priv->redirect != NULL) {
                g_object_unref (composer->priv->redirect);
@@ -462,10 +415,6 @@ e_composer_private_finalize (EMsgComposer *composer)
        g_free (composer->priv->charset);
        g_free (composer->priv->mime_type);
        g_free (composer->priv->mime_body);
-       g_free (composer->priv->selected_signature_uid);
-
-       g_hash_table_destroy (composer->priv->inline_images);
-       g_hash_table_destroy (composer->priv->inline_images_by_url);
 }
 
 gchar *
@@ -525,92 +474,13 @@ e_composer_get_default_charset (void)
        return charset;
 }
 
-gchar *
-e_composer_decode_clue_value (const gchar *encoded_value)
-{
-       GString *buffer;
-       const gchar *cp;
-
-       /* Decode a GtkHtml "ClueFlow" value. */
-
-       g_return_val_if_fail (encoded_value != NULL, NULL);
-
-       buffer = g_string_sized_new (strlen (encoded_value));
-
-       /* Copy the value, decoding escaped characters as we go. */
-       cp = encoded_value;
-       while (*cp != '\0') {
-               if (*cp == '.') {
-                       cp++;
-                       switch (*cp) {
-                               case '.':
-                                       g_string_append_c (buffer, '.');
-                                       break;
-                               case '1':
-                                       g_string_append_c (buffer, '"');
-                                       break;
-                               case '2':
-                                       g_string_append_c (buffer, '=');
-                                       break;
-                               default:
-                                       /* Invalid escape sequence. */
-                                       g_string_free (buffer, TRUE);
-                                       return NULL;
-                       }
-               } else
-                       g_string_append_c (buffer, *cp);
-               cp++;
-       }
-
-       return g_string_free (buffer, FALSE);
-}
-
-gchar *
-e_composer_encode_clue_value (const gchar *decoded_value)
-{
-       gchar *encoded_value;
-       gchar **strv;
-
-       /* Encode a GtkHtml "ClueFlow" value. */
-
-       g_return_val_if_fail (decoded_value != NULL, NULL);
-
-       /* XXX This is inefficient but easy to understand. */
-
-       encoded_value = g_strdup (decoded_value);
-
-       /* Substitution: '.' --> '..' (do this first) */
-       if (strchr (encoded_value, '.') != NULL) {
-               strv = g_strsplit (encoded_value, ".", 0);
-               g_free (encoded_value);
-               encoded_value = g_strjoinv ("..", strv);
-               g_strfreev (strv);
-       }
-
-       /* Substitution: '"' --> '.1' */
-       if (strchr (encoded_value, '"') != NULL) {
-               strv = g_strsplit (encoded_value, """", 0);
-               g_free (encoded_value);
-               encoded_value = g_strjoinv (".1", strv);
-               g_strfreev (strv);
-       }
-
-       /* Substitution: '=' --> '.2' */
-       if (strchr (encoded_value, '=') != NULL) {
-               strv = g_strsplit (encoded_value, "=", 0);
-               g_free (encoded_value);
-               encoded_value = g_strjoinv (".2", strv);
-               g_strfreev (strv);
-       }
-
-       return encoded_value;
-}
-
 gboolean
 e_composer_paste_html (EMsgComposer *composer,
                        GtkClipboard *clipboard)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *editor_selection;
        gchar *html;
 
        g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
@@ -619,9 +489,15 @@ e_composer_paste_html (EMsgComposer *composer,
        html = e_clipboard_wait_for_html (clipboard);
        g_return_val_if_fail (html != NULL, FALSE);
 
-       editor = GTKHTML_EDITOR (composer);
-       gtkhtml_editor_insert_html (editor, html);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       editor_selection = e_html_editor_view_get_selection (view);
+       e_html_editor_selection_insert_html (editor_selection, html);
 
+       e_html_editor_view_check_magic_links (view, FALSE);
+       e_html_editor_view_force_spell_check (view);
+
+       e_html_editor_selection_scroll_to_caret (editor_selection);
        g_free (html);
 
        return TRUE;
@@ -631,7 +507,8 @@ gboolean
 e_composer_paste_image (EMsgComposer *composer,
                         GtkClipboard *clipboard)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *html_editor_view;
        EAttachmentStore *store;
        EAttachmentView *view;
        GdkPixbuf *pixbuf = NULL;
@@ -643,7 +520,6 @@ e_composer_paste_image (EMsgComposer *composer,
        g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
        g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE);
 
-       editor = GTKHTML_EDITOR (composer);
        view = e_msg_composer_get_attachment_view (composer);
        store = e_attachment_view_get_store (view);
 
@@ -673,9 +549,15 @@ e_composer_paste_image (EMsgComposer *composer,
 
        /* In HTML mode, paste the image into the message body.
         * In text mode, add the image to the attachment store. */
-       if (gtkhtml_editor_get_html_mode (editor))
-               gtkhtml_editor_insert_image (editor, uri);
-       else {
+       editor = e_msg_composer_get_editor (composer);
+       html_editor_view = e_html_editor_get_view (editor);
+       if (e_html_editor_view_get_html_mode (html_editor_view)) {
+               EHTMLEditorSelection *selection;
+
+               selection = e_html_editor_view_get_selection (html_editor_view);
+               e_html_editor_selection_insert_image (selection, uri);
+               e_html_editor_selection_scroll_to_caret (selection);
+       } else {
                EAttachment *attachment;
 
                attachment = e_attachment_new_for_uri (uri);
@@ -705,7 +587,9 @@ gboolean
 e_composer_paste_text (EMsgComposer *composer,
                        GtkClipboard *clipboard)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *editor_selection;
        gchar *text;
 
        g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
@@ -714,8 +598,18 @@ e_composer_paste_text (EMsgComposer *composer,
        text = gtk_clipboard_wait_for_text (clipboard);
        g_return_val_if_fail (text != NULL, FALSE);
 
-       editor = GTKHTML_EDITOR (composer);
-       gtkhtml_editor_insert_text (editor, text);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       editor_selection = e_html_editor_view_get_selection (view);
+       /* If WebView doesn't have focus, focus it */
+       if (!gtk_widget_has_focus (GTK_WIDGET (view)))
+               gtk_widget_grab_focus (GTK_WIDGET (view));
+
+       e_html_editor_selection_insert_text (editor_selection, text);
+
+       e_html_editor_view_check_magic_links (view, FALSE);
+       e_html_editor_view_force_spell_check (view);
+       e_html_editor_selection_scroll_to_caret (editor_selection);
 
        g_free (text);
 
@@ -757,6 +651,35 @@ e_composer_paste_uris (EMsgComposer *composer,
 }
 
 gboolean
+e_composer_selection_is_base64_uris (EMsgComposer *composer,
+                                     GtkSelectionData *selection)
+{
+       gboolean all_base64_uris = TRUE;
+       gchar **uris;
+       guint ii;
+
+       g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
+       g_return_val_if_fail (selection != NULL, FALSE);
+
+       uris = gtk_selection_data_get_uris (selection);
+
+       if (!uris)
+               return FALSE;
+
+       for (ii = 0; uris[ii] != NULL; ii++) {
+               if (!((g_str_has_prefix (uris[ii], "data:") || strstr (uris[ii], ";data:"))
+                   && strstr (uris[ii], ";base64,"))) {
+                       all_base64_uris = FALSE;
+                       break;
+               }
+       }
+
+       g_strfreev (uris);
+
+       return all_base64_uris;
+}
+
+gboolean
 e_composer_selection_is_image_uris (EMsgComposer *composer,
                                     GtkSelectionData *selection)
 {
@@ -769,7 +692,7 @@ e_composer_selection_is_image_uris (EMsgComposer *composer,
 
        uris = gtk_selection_data_get_uris (selection);
 
-       if (uris == NULL)
+       if (!uris)
                return FALSE;
 
        for (ii = 0; uris[ii] != NULL; ii++) {
@@ -859,19 +782,255 @@ use_top_signature (EMsgComposer *composer)
 }
 
 static void
+composer_size_allocate_cb (GtkWidget *widget,
+                          gpointer user_data)
+{
+       GtkWidget *scrolled_window;
+       GtkAdjustment *adj;
+
+       scrolled_window = gtk_widget_get_parent (GTK_WIDGET (widget));
+       adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window));
+
+       /* Scroll only when there is some size allocated */
+       if (gtk_adjustment_get_upper (adj) != 0.0) {
+               /* Scroll web view down to caret */
+               gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size 
(adj));
+               gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window), adj);
+               /* Disconnect because we don't want to scroll down the view on every window size change */
+               g_signal_handlers_disconnect_by_func (
+                       widget, G_CALLBACK (composer_size_allocate_cb), NULL);
+       }
+}
+
+static void
+insert_paragraph_with_input (WebKitDOMElement *paragraph,
+                             WebKitDOMElement *body)
+{
+       WebKitDOMNode *node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+       if (node) {
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (body),
+                       WEBKIT_DOM_NODE (paragraph),
+                       node,
+                       NULL);
+       } else {
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (body),
+                       WEBKIT_DOM_NODE (paragraph),
+                       NULL);
+       }
+}
+
+static void
+composer_move_caret (EMsgComposer *composer)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *editor_selection;
+       GSettings *settings;
+       gboolean start_bottom, html_mode, top_signature;
+       gboolean has_paragraphs_in_body = TRUE;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMElement *input_start, *element, *signature;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNodeList *list, *blockquotes;
+       WebKitDOMRange *new_range;
+
+       /* When there is an option composer-reply-start-bottom set we have
+        * to move the caret between reply and signature. */
+       settings = g_settings_new ("org.gnome.evolution.mail");
+       start_bottom = g_settings_get_boolean (settings, "composer-reply-start-bottom");
+       g_object_unref (settings);
+
+       top_signature =
+               use_top_signature (composer) &&
+               !composer->priv->is_from_message &&
+               !composer->priv->is_from_new_message;
+
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       editor_selection = e_html_editor_view_get_selection (view);
+       html_mode = e_html_editor_view_get_html_mode (view);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+
+       body = webkit_dom_document_get_body (document);
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-message", "", NULL);
+       new_range = webkit_dom_document_create_range (document);
+
+       element = webkit_dom_document_get_element_by_id (document, "-x-evo-caret-position");
+       /* Caret position found => composer mode changed */
+       if (element) {
+               e_html_editor_selection_restore_caret_position (editor_selection);
+               /* We want to force spellcheck just in case that we switched to plain
+                * text mode (when switching to html mode, the underlined words are
+                * preserved */
+               if (!html_mode)
+                       e_html_editor_view_force_spell_check (view);
+               return;
+       }
+
+       /* If editing message as new don't handle with caret */
+       if (composer->priv->is_from_message || composer->priv->is_from_draft) {
+               if (composer->priv->is_from_message)
+                       webkit_dom_element_set_attribute (
+                               WEBKIT_DOM_ELEMENT (body),
+                               "data-edit-as-new",
+                               "",
+                               NULL);
+               e_html_editor_selection_restore_caret_position (editor_selection);
+               e_html_editor_selection_scroll_to_caret (editor_selection);
+
+               e_html_editor_view_force_spell_check (view);
+               return;
+       }
+
+       e_html_editor_selection_block_selection_changed (editor_selection);
+
+       /* When the new message is written from the beginning - note it into body */
+       if (composer->priv->is_from_new_message) {
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (body), "data-new-message", "", NULL);
+       }
+
+       list = webkit_dom_document_get_elements_by_class_name (document, "-x-evo-paragraph");
+       signature = webkit_dom_document_query_selector (document, ".-x-evo-signature", NULL);
+       /* Situation when wrapped paragraph is just in signature and not in message body */
+       if (webkit_dom_node_list_get_length (list) == 1) {
+               if (signature && webkit_dom_element_query_selector (signature, ".-x-evo-paragraph", NULL))
+                       has_paragraphs_in_body = FALSE;
+       }
+
+       if (webkit_dom_node_list_get_length (list) == 0)
+               has_paragraphs_in_body = FALSE;
+
+       blockquotes = webkit_dom_document_get_elements_by_tag_name (document, "blockquote");
+
+       if (!has_paragraphs_in_body) {
+               element = e_html_editor_selection_get_paragraph_element (
+                       editor_selection, document, -1, 0);
+               webkit_dom_element_set_id (element, "-x-evo-input-start");
+               webkit_dom_html_element_set_inner_html (
+                       WEBKIT_DOM_HTML_ELEMENT (element), UNICODE_ZERO_WIDTH_SPACE, NULL);
+               if (top_signature)
+                       element_add_class (element, "-x-evo-top-signature");
+       }
+
+       if (start_bottom) {
+               if (webkit_dom_node_list_get_length (blockquotes) != 0) {
+                       if (!has_paragraphs_in_body) {
+                               if (!top_signature) {
+                                       webkit_dom_node_insert_before (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (element),
+                                               signature ?
+                                                       webkit_dom_node_get_parent_node (
+                                                               WEBKIT_DOM_NODE (signature)) :
+                                                       webkit_dom_node_get_next_sibling (
+                                                               webkit_dom_node_list_item (
+                                                                       blockquotes, 0)),
+                                               NULL);
+                               } else {
+                                       webkit_dom_node_append_child (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (element),
+                                               NULL);
+                               }
+                       }
+
+                       e_html_editor_selection_restore_caret_position (editor_selection);
+                       if (!html_mode)
+                               e_html_editor_view_quote_plain_text (view);
+                       e_html_editor_view_force_spell_check (view);
+
+                       input_start = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-input-start");
+                       if (input_start)
+                               webkit_dom_range_select_node_contents (
+                                       new_range, WEBKIT_DOM_NODE (input_start), NULL);
+
+                       webkit_dom_range_collapse (new_range, FALSE, NULL);
+               } else {
+                       if (!has_paragraphs_in_body)
+                               insert_paragraph_with_input (
+                                       element, WEBKIT_DOM_ELEMENT (body));
+
+                       webkit_dom_range_select_node_contents (
+                               new_range,
+                               webkit_dom_node_get_first_child (
+                                       WEBKIT_DOM_NODE (body)),
+                               NULL);
+                       webkit_dom_range_collapse (new_range, TRUE, NULL);
+               }
+
+               g_signal_connect (
+                       view, "size-allocate",
+                       G_CALLBACK (composer_size_allocate_cb), NULL);
+       } else {
+               /* Move caret on the beginning of message */
+               if (!has_paragraphs_in_body) {
+                       insert_paragraph_with_input (
+                               element, WEBKIT_DOM_ELEMENT (body));
+
+                       if (webkit_dom_node_list_get_length (blockquotes) != 0) {
+                               if (!html_mode) {
+                                       WebKitDOMNode *blockquote;
+
+                                       blockquote = webkit_dom_node_list_item (blockquotes, 0);
+
+                                       /* FIXME determine when we can skip this */
+                                       e_html_editor_selection_wrap_paragraph (
+                                               editor_selection,
+                                               WEBKIT_DOM_ELEMENT (blockquote));
+
+                                       e_html_editor_selection_restore_caret_position (editor_selection);
+                                       e_html_editor_view_quote_plain_text (view);
+                                       body = webkit_dom_document_get_body (document);
+                               }
+                       }
+               }
+
+               e_html_editor_view_force_spell_check (view);
+
+               webkit_dom_range_select_node_contents (
+                       new_range,
+                       WEBKIT_DOM_NODE (
+                               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body))),
+                       NULL);
+               webkit_dom_range_collapse (new_range, TRUE, NULL);
+       }
+
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, new_range);
+
+       e_html_editor_selection_unblock_selection_changed (editor_selection);
+}
+
+static void
 composer_load_signature_cb (EMailSignatureComboBox *combo_box,
                             GAsyncResult *result,
                             EMsgComposer *composer)
 {
        GString *html_buffer = NULL;
-       GtkhtmlEditor *editor;
        gchar *contents = NULL;
        gsize length = 0;
        const gchar *active_id;
-       gchar *encoded_uid = NULL;
        gboolean top_signature;
        gboolean is_html;
        GError *error = NULL;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *signatures;
+       gulong list_length, ii;
+       GSettings *settings;
+       gboolean start_bottom;
 
        e_mail_signature_combo_box_load_selected_finish (
                combo_box, result, &contents, &length, &is_html, &error);
@@ -887,7 +1046,12 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
         * Always put the signature at the bottom for that case. */
        top_signature =
                use_top_signature (composer) &&
-               !composer->priv->is_from_message;
+               !composer->priv->is_from_message &&
+               !composer->priv->is_from_new_message;
+
+       settings = g_settings_new ("org.gnome.evolution.mail");
+       start_bottom = g_settings_get_boolean (settings, "composer-reply-start-bottom");
+       g_object_unref (settings);
 
        if (contents == NULL)
                goto insert;
@@ -911,24 +1075,13 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
        /* The combo box active ID is the signature's ESource UID. */
        active_id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo_box));
 
-       if (active_id != NULL && *active_id != '\0')
-               encoded_uid = e_composer_encode_clue_value (active_id);
-
        g_string_append_printf (
                html_buffer,
-               "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
-               "    key=\"signature\" value=\"1\">-->"
-               "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
-               "    key=\"signature_name\" value=\"uid:%s\">-->",
-               (encoded_uid != NULL) ? encoded_uid : "");
-
-       g_string_append (
-               html_buffer,
-               "<TABLE WIDTH=\"100%%\" CELLSPACING=\"0\""
-               " CELLPADDING=\"0\"><TR><TD>");
+               "<SPAN class=\"-x-evo-signature\" id=\"1\" name=\"%s\">",
+               (active_id != NULL) ? active_id : "");
 
        if (!is_html)
-               g_string_append (html_buffer, "<PRE>\n");
+               g_string_append (html_buffer, "<PRE>");
 
        /* The signature dash convention ("-- \n") is specified
         * in the "Son of RFC 1036", section 4.3.2.
@@ -939,8 +1092,8 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
                const gchar *delim_nl;
 
                if (is_html) {
-                       delim = "-- \n<BR>";
-                       delim_nl = "\n-- \n<BR>";
+                       delim = "-- <BR>";
+                       delim_nl = "\n-- <BR>";
                } else {
                        delim = "-- \n";
                        delim_nl = "\n-- \n";
@@ -958,73 +1111,148 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
        g_string_append_len (html_buffer, contents, length);
 
        if (!is_html)
-               g_string_append (html_buffer, "</PRE>\n");
-
-       if (top_signature)
-               g_string_append (html_buffer, "<BR>");
-
-       g_string_append (html_buffer, "</TD></TR></TABLE>");
+               g_string_append (html_buffer, "</PRE>");
 
-       g_free (encoded_uid);
+       g_string_append (html_buffer, "</SPAN>");
        g_free (contents);
 
 insert:
        /* Remove the old signature and insert the new one. */
 
-       editor = GTKHTML_EDITOR (composer);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       signatures = webkit_dom_document_get_elements_by_class_name (
+               document, "-x-evo-signature");
+       list_length = webkit_dom_node_list_get_length (signatures);
+       for (ii = 0; ii < list_length; ii++) {
+               WebKitDOMNode *node;
+               gchar *id;
+
+               node = webkit_dom_node_list_item (signatures, ii);
+               id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (node));
+
+               /* When we are editing a message with signature we need to set active
+                * signature id in signature combo box otherwise no signature will be
+                * added but we have to do it just once when the composer opens */
+               if (composer->priv->is_from_message && composer->priv->set_signature_from_message) {
+                       gchar *name = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "name");
+                       gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), name);
+                       g_free (name);
+                       composer->priv->set_signature_from_message = FALSE;
+               }
+
+               if (id && (strlen (id) == 1) && (*id == '1')) {
+                       /* We have to remove the div containing the span with signature */
+                       WebKitDOMNode *next_sibling;
+                       WebKitDOMNode *parent;
+
+                       parent = webkit_dom_node_get_parent_node (node);
+                       next_sibling = webkit_dom_node_get_next_sibling (parent);
 
-       /* This prevents our command before/after callbacks from
-        * screwing around with the signature as we insert it. */
-       composer->priv->in_signature_insert = TRUE;
+                       if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling))
+                               webkit_dom_node_remove_child (
+                                       webkit_dom_node_get_parent_node (next_sibling),
+                                       next_sibling,
+                                       NULL);
 
-       gtkhtml_editor_freeze (editor);
-       gtkhtml_editor_run_command (editor, "cursor-position-save");
-       gtkhtml_editor_undo_begin (editor, "Set signature", "Reset signature");
+                       webkit_dom_node_remove_child (
+                               webkit_dom_node_get_parent_node (parent),
+                               parent,
+                               NULL);
+
+                       g_free (id);
+                       break;
+               }
 
-       gtkhtml_editor_run_command (editor, "block-selection");
-       gtkhtml_editor_run_command (editor, "cursor-bod");
-       if (gtkhtml_editor_search_by_data (editor, 1, "ClueFlow", "signature", "1")) {
-               gtkhtml_editor_run_command (editor, "select-paragraph");
-               gtkhtml_editor_run_command (editor, "delete");
-               gtkhtml_editor_set_paragraph_data (editor, "signature", "0");
-               gtkhtml_editor_run_command (editor, "delete-back");
+               g_free (id);
        }
-       gtkhtml_editor_run_command (editor, "unblock-selection");
 
        if (html_buffer != NULL) {
-               gtkhtml_editor_run_command (editor, "insert-paragraph");
-               if (!gtkhtml_editor_run_command (editor, "cursor-backward"))
-                       gtkhtml_editor_run_command (editor, "insert-paragraph");
-               else
-                       gtkhtml_editor_run_command (editor, "cursor-forward");
-
-               gtkhtml_editor_set_paragraph_data (editor, "orig", "0");
-               gtkhtml_editor_run_command (editor, "indent-zero");
-               gtkhtml_editor_run_command (editor, "style-normal");
-               gtkhtml_editor_insert_html (editor, html_buffer->str);
+               if (*html_buffer->str) {
+                       WebKitDOMElement *element;
+                       WebKitDOMHTMLElement *body;
+
+                       body = webkit_dom_document_get_body (document);
+                       element = webkit_dom_document_create_element (document, "DIV", NULL);
+
+                       webkit_dom_html_element_set_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (element), html_buffer->str, NULL);
+
+                       if (top_signature) {
+                               WebKitDOMNode *signature_inserted;
+                               WebKitDOMNode *child =
+                                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+                               WebKitDOMElement *br =
+                                       webkit_dom_document_create_element (
+                                               document, "br", NULL);
+
+                               if (start_bottom) {
+                                       signature_inserted = webkit_dom_node_insert_before (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (element),
+                                               child,
+                                               NULL);
+                               } else {
+                                       WebKitDOMElement *input_start =
+                                               webkit_dom_document_get_element_by_id (
+                                                       document, "-x-evo-input-start");
+                                       /* When we are using signature on top the caret
+                                        * should be before the signature */
+                                       signature_inserted = webkit_dom_node_insert_before (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (element),
+                                               input_start ?
+                                                       webkit_dom_node_get_next_sibling (
+                                                               WEBKIT_DOM_NODE (input_start)) :
+                                                       child,
+                                               NULL);
+                               }
+
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (br),
+                                       webkit_dom_node_get_next_sibling (signature_inserted),
+                                       NULL);
+                       } else {
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (element),
+                                       NULL);
+                       }
+               }
 
                g_string_free (html_buffer, TRUE);
-
-       } else if (top_signature) {
-               /* Insert paragraph after the signature ClueFlow stuff. */
-               if (gtkhtml_editor_run_command (editor, "cursor-forward"))
-                       gtkhtml_editor_run_command (editor, "insert-paragraph");
        }
 
-       gtkhtml_editor_undo_end (editor);
-       gtkhtml_editor_run_command (editor, "cursor-position-restore");
-       gtkhtml_editor_thaw (editor);
-
-       composer->priv->in_signature_insert = FALSE;
+       composer_move_caret (composer);
 
 exit:
        g_object_unref (composer);
 }
 
-static gboolean
-is_null_or_none (const gchar *text)
+static void
+composer_web_view_load_status_changed_cb (WebKitWebView *webkit_web_view,
+                                         GParamSpec *pspec,
+                                         EMsgComposer *composer)
 {
-       return !text || g_strcmp0 (text, "none") == 0;
+       WebKitLoadStatus status;
+
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
+       status = webkit_web_view_get_load_status (webkit_web_view);
+
+       if (status != WEBKIT_LOAD_FINISHED)
+               return;
+
+       g_signal_handlers_disconnect_by_func (
+               webkit_web_view,
+               G_CALLBACK (composer_web_view_load_status_changed_cb),
+               NULL);
+
+       e_composer_update_signature (composer);
 }
 
 void
@@ -1032,29 +1260,35 @@ e_composer_update_signature (EMsgComposer *composer)
 {
        EComposerHeaderTable *table;
        EMailSignatureComboBox *combo_box;
-       const gchar *signature_uid;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       WebKitLoadStatus status;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
-       /* Do nothing if we're redirecting a message. */
-       if (composer->priv->redirect)
+       /* Do nothing if we're redirecting a message or we disabled the signature * on purpose */
+       if (composer->priv->redirect || composer->priv->disable_signature)
                return;
 
        table = e_msg_composer_get_header_table (composer);
-       signature_uid = e_composer_header_table_get_signature_uid (table);
-
-       /* this is a case when the signature combo cleared itself for a reload */
-       if (!signature_uid)
-               return;
-
-       if (g_strcmp0 (signature_uid, composer->priv->selected_signature_uid) == 0 ||
-           (is_null_or_none (signature_uid) && is_null_or_none (composer->priv->selected_signature_uid)))
-               return;
-
-       g_free (composer->priv->selected_signature_uid);
-       composer->priv->selected_signature_uid = g_strdup (signature_uid);
-
        combo_box = e_composer_header_table_get_signature_combo_box (table);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+
+       status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (view));
+       /* If document is not loaded, we will wait for him */
+       if (status != WEBKIT_LOAD_FINISHED) {
+               /* Disconnect previous handlers */
+               g_signal_handlers_disconnect_by_func (
+                       WEBKIT_WEB_VIEW (view),
+                       G_CALLBACK (composer_web_view_load_status_changed_cb),
+                       composer);
+               g_signal_connect (
+                       WEBKIT_WEB_VIEW(view), "notify::load-status",
+                       G_CALLBACK (composer_web_view_load_status_changed_cb),
+                       composer);
+               return;
+       }
 
        /* XXX Signature files should be local and therefore load quickly,
         *     so while we do load them asynchronously we don't allow for
diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h
index b09e025..46c72b7 100644
--- a/composer/e-composer-private.h
+++ b/composer/e-composer-private.h
@@ -32,7 +32,6 @@
 #include <libebackend/libebackend.h>
 
 #include "e-composer-actions.h"
-#include "e-composer-activity.h"
 #include "e-composer-header-table.h"
 
 #ifdef HAVE_XFREE
@@ -58,11 +57,11 @@ struct _EMsgComposerPrivate {
 
        gpointer shell;  /* weak pointer */
 
+       EHTMLEditor *editor;
+
        /*** UI Management ***/
 
        GtkWidget *header_table;
-       GtkWidget *activity_bar;
-       GtkWidget *alert_bar;
        GtkWidget *attachment_paned;
 
        EFocusTracker *focus_tracker;
@@ -82,10 +81,6 @@ struct _EMsgComposerPrivate {
 
        GtkWidget *address_dialog;
 
-       GHashTable *inline_images;
-       GHashTable *inline_images_by_url;
-       GList *current_images;
-
        gchar *mime_type;
        gchar *mime_body;
        gchar *charset;
@@ -97,9 +92,18 @@ struct _EMsgComposerPrivate {
 
        CamelMimeMessage *redirect;
 
+       gboolean busy;
+       gboolean disable_signature;
+       gboolean is_from_draft;
        gboolean is_from_message;
-
-       gchar *selected_signature_uid;
+       gboolean is_from_new_message;
+       /* The web view is uneditable while the editor is busy.
+        * This is used to restore the previous editable state. */
+       gboolean saved_editable;
+       gboolean set_signature_from_message;
+
+       gint focused_entry_selection_start;
+       gint focused_entry_selection_end;
 };
 
 void           e_composer_private_constructed  (EMsgComposer *composer);
@@ -121,6 +125,9 @@ gboolean    e_composer_paste_text           (EMsgComposer *composer,
                                                 GtkClipboard *clipboard);
 gboolean       e_composer_paste_uris           (EMsgComposer *composer,
                                                 GtkClipboard *clipboard);
+gboolean       e_composer_selection_is_base64_uris
+                                               (EMsgComposer *composer,
+                                                GtkSelectionData *selection);
 gboolean       e_composer_selection_is_image_uris
                                                (EMsgComposer *composer,
                                                 GtkSelectionData *selection);
diff --git a/composer/e-composer-spell-header.c b/composer/e-composer-spell-header.c
index c4fc471..b2d2dfa 100644
--- a/composer/e-composer-spell-header.c
+++ b/composer/e-composer-spell-header.c
@@ -63,16 +63,3 @@ e_composer_spell_header_new_button (ESourceRegistry *registry,
                "registry", registry, NULL);
 }
 
-void
-e_composer_spell_header_set_languages (EComposerSpellHeader *header,
-                                       GList *languages)
-{
-       ESpellEntry *spell_entry;
-
-       g_return_if_fail (header != NULL);
-
-       spell_entry = E_SPELL_ENTRY (E_COMPOSER_HEADER (header)->input_widget);
-       g_return_if_fail (spell_entry != NULL);
-
-       e_spell_entry_set_languages (spell_entry, languages);
-}
diff --git a/composer/e-composer-spell-header.h b/composer/e-composer-spell-header.h
index d440ee1..f0034a0 100644
--- a/composer/e-composer-spell-header.h
+++ b/composer/e-composer-spell-header.h
@@ -66,9 +66,6 @@ EComposerHeader *
                e_composer_spell_header_new_button
                                                (ESourceRegistry *registry,
                                                 const gchar *label);
-void           e_composer_spell_header_set_languages
-                                               (EComposerSpellHeader *header,
-                                                GList *languages);
 
 G_END_DECLS
 
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index 57b716c..d3891be 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -35,6 +35,7 @@
 #include <unistd.h>
 #include <ctype.h>
 #include <fcntl.h>
+#include <enchant/enchant.h>
 
 #include "e-composer-private.h"
 
@@ -72,19 +73,22 @@ struct _AsyncContext {
 
 /* Flags for building a message. */
 typedef enum {
-       COMPOSER_FLAG_HTML_CONTENT = 1 << 0,
-       COMPOSER_FLAG_SAVE_OBJECT_DATA = 1 << 1,
-       COMPOSER_FLAG_PRIORITIZE_MESSAGE = 1 << 2,
-       COMPOSER_FLAG_REQUEST_READ_RECEIPT = 1 << 3,
-       COMPOSER_FLAG_PGP_SIGN = 1 << 4,
-       COMPOSER_FLAG_PGP_ENCRYPT = 1 << 5,
-       COMPOSER_FLAG_SMIME_SIGN = 1 << 6,
-       COMPOSER_FLAG_SMIME_ENCRYPT = 1 << 7,
-       COMPOSER_FLAG_DRAFT = 1 << 8
+       COMPOSER_FLAG_HTML_CONTENT              = 1 << 0,
+       COMPOSER_FLAG_SAVE_OBJECT_DATA          = 1 << 1,
+       COMPOSER_FLAG_PRIORITIZE_MESSAGE        = 1 << 2,
+       COMPOSER_FLAG_REQUEST_READ_RECEIPT      = 1 << 3,
+       COMPOSER_FLAG_PGP_SIGN                  = 1 << 4,
+       COMPOSER_FLAG_PGP_ENCRYPT               = 1 << 5,
+       COMPOSER_FLAG_SMIME_SIGN                = 1 << 6,
+       COMPOSER_FLAG_SMIME_ENCRYPT             = 1 << 7,
+       COMPOSER_FLAG_HTML_MODE                 = 1 << 8,
+       COMPOSER_FLAG_SAVE_DRAFT                = 1 << 9
 } ComposerFlags;
 
 enum {
        PROP_0,
+       PROP_BUSY,
+       PROP_EDITOR,
        PROP_FOCUS_TRACKER,
        PROP_SHELL
 };
@@ -98,6 +102,24 @@ enum {
        LAST_SIGNAL
 };
 
+enum DndTargetType {
+       DND_TARGET_TYPE_TEXT_URI_LIST,
+       DND_TARGET_TYPE_MOZILLA_URL,
+       DND_TARGET_TYPE_TEXT_HTML,
+       DND_TARGET_TYPE_UTF8_STRING,
+       DND_TARGET_TYPE_TEXT_PLAIN,
+       DND_TARGET_TYPE_STRING
+};
+
+static GtkTargetEntry drag_dest_targets[] = {
+       { (gchar *) "text/uri-list", 0, DND_TARGET_TYPE_TEXT_URI_LIST },
+       { (gchar *) "_NETSCAPE_URL", 0, DND_TARGET_TYPE_MOZILLA_URL },
+       { (gchar *) "text/html", 0, DND_TARGET_TYPE_TEXT_HTML },
+       { (gchar *) "UTF8_STRING", 0, DND_TARGET_TYPE_UTF8_STRING },
+       { (gchar *) "text/plain", 0, DND_TARGET_TYPE_TEXT_PLAIN },
+       { (gchar *) "STRING", 0, DND_TARGET_TYPE_STRING },
+};
+
 static guint signals[LAST_SIGNAL];
 
 /* used by e_msg_composer_add_message_attachments () */
@@ -128,14 +150,10 @@ static void       handle_multipart_signed         (EMsgComposer *composer,
                                                 GCancellable *cancellable,
                                                 gint depth);
 
-static void    e_msg_composer_alert_sink_init  (EAlertSinkInterface *iface);
-
 G_DEFINE_TYPE_WITH_CODE (
        EMsgComposer,
        e_msg_composer,
-       GTKHTML_TYPE_EDITOR,
-       G_IMPLEMENT_INTERFACE (
-               E_TYPE_ALERT_SINK, e_msg_composer_alert_sink_init)
+       GTK_TYPE_WINDOW,
        G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
 
 static void
@@ -407,45 +425,6 @@ best_charset (GByteArray *buf,
        return g_strdup (charset);
 }
 
-static void
-clear_current_images (EMsgComposer *composer)
-{
-       EMsgComposerPrivate *p = composer->priv;
-       g_list_free (p->current_images);
-       p->current_images = NULL;
-}
-
-void
-e_msg_composer_clear_inlined_table (EMsgComposer *composer)
-{
-       EMsgComposerPrivate *p = composer->priv;
-
-       g_hash_table_remove_all (p->inline_images);
-       g_hash_table_remove_all (p->inline_images_by_url);
-}
-
-static void
-add_inlined_images (EMsgComposer *composer,
-                    CamelMultipart *multipart)
-{
-       EMsgComposerPrivate *p = composer->priv;
-
-       GList *d = p->current_images;
-       GHashTable *added;
-
-       added = g_hash_table_new (g_direct_hash, g_direct_equal);
-       while (d) {
-               CamelMimePart *part = d->data;
-
-               if (!g_hash_table_lookup (added, part)) {
-                       camel_multipart_add_part (multipart, part);
-                       g_hash_table_insert (added, part, part);
-               }
-               d = d->next;
-       }
-       g_hash_table_destroy (added);
-}
-
 /* These functions builds a CamelMimeMessage for the message that the user has
  * composed in 'composer'.
  */
@@ -1041,6 +1020,25 @@ composer_build_message_thread (GSimpleAsyncResult *simple,
 }
 
 static void
+composer_add_evolution_composer_mode_header (CamelMedium *medium,
+                                             ComposerFlags flags)
+{
+       GString *string;
+
+       string = g_string_sized_new (128);
+
+       if (flags & COMPOSER_FLAG_HTML_MODE)
+               g_string_append (string, "text/html");
+       else
+               g_string_append (string, "text/plain");
+
+       camel_medium_add_header (
+               medium, "X-Evolution-Composer-Mode", string->str);
+
+       g_string_free (string, TRUE);
+}
+
+static void
 composer_add_evolution_format_header (CamelMedium *medium,
                                       ComposerFlags flags)
 {
@@ -1082,7 +1080,6 @@ composer_build_message (EMsgComposer *composer,
        EMsgComposerPrivate *priv;
        GSimpleAsyncResult *simple;
        AsyncContext *context;
-       GtkhtmlEditor *editor;
        EAttachmentView *view;
        EAttachmentStore *store;
        EComposerHeaderTable *table;
@@ -1103,7 +1100,6 @@ composer_build_message (EMsgComposer *composer,
        gint i;
 
        priv = composer->priv;
-       editor = GTKHTML_EDITOR (composer);
        table = e_msg_composer_get_header_table (composer);
        view = e_msg_composer_get_attachment_view (composer);
        store = e_attachment_view_get_store (view);
@@ -1120,19 +1116,17 @@ composer_build_message (EMsgComposer *composer,
        context->session = e_msg_composer_ref_session (composer);
        context->from = e_msg_composer_get_from (composer);
 
-       if ((flags & COMPOSER_FLAG_DRAFT) == 0) {
-               if ((flags & COMPOSER_FLAG_PGP_SIGN) != 0)
-                       context->pgp_sign = TRUE;
+       if (flags & COMPOSER_FLAG_PGP_SIGN)
+               context->pgp_sign = TRUE;
 
-               if ((flags & COMPOSER_FLAG_PGP_ENCRYPT) != 0)
-                       context->pgp_encrypt = TRUE;
+       if (flags & COMPOSER_FLAG_PGP_ENCRYPT)
+               context->pgp_encrypt = TRUE;
 
-               if ((flags & COMPOSER_FLAG_SMIME_SIGN) != 0)
-                       context->smime_sign = TRUE;
+       if (flags & COMPOSER_FLAG_SMIME_SIGN)
+               context->smime_sign = TRUE;
 
-               if ((flags & COMPOSER_FLAG_SMIME_ENCRYPT) != 0)
-                       context->smime_encrypt = TRUE;
-       }
+       if (flags & COMPOSER_FLAG_SMIME_ENCRYPT)
+               context->smime_encrypt = TRUE;
 
        context->need_thread =
                context->pgp_sign || context->pgp_encrypt ||
@@ -1210,6 +1204,41 @@ composer_build_message (EMsgComposer *composer,
        composer_add_evolution_format_header (
                CAMEL_MEDIUM (context->message), flags);
 
+       /* X-Evolution-Composer-Mode */
+       composer_add_evolution_composer_mode_header (
+               CAMEL_MEDIUM (context->message), flags);
+
+       if (flags & COMPOSER_FLAG_SAVE_DRAFT) {
+               gchar *text;
+               EHTMLEditor *editor;
+               EHTMLEditorView *view;
+               EHTMLEditorSelection *selection;
+
+               editor = e_msg_composer_get_editor (composer);
+               view = e_html_editor_get_view (editor);
+               selection = e_html_editor_view_get_selection (view);
+
+               data = g_byte_array_new ();
+
+               e_html_editor_view_embed_styles (view);
+               e_html_editor_selection_save_caret_position (selection);
+
+               text = e_html_editor_view_get_text_html_for_drafts (view);
+
+               e_html_editor_view_remove_embed_styles (view);
+               e_html_editor_selection_restore_caret_position (selection);
+
+               g_byte_array_append (data, (guint8 *) text, strlen (text));
+
+               g_free (text);
+
+               type = camel_content_type_new ("text", "html");
+               camel_content_type_set_param (type, "charset", "utf-8");
+               iconv_charset = camel_iconv_charset_name ("utf-8");
+
+               goto wrap_drafts_html;
+       }
+
        /* Build the text/plain part. */
 
        if (priv->mime_body) {
@@ -1235,11 +1264,14 @@ composer_build_message (EMsgComposer *composer,
 
        } else {
                gchar *text;
-               gsize length;
+               EHTMLEditor *editor;
+               EHTMLEditorView *view;
 
+               editor = e_msg_composer_get_editor (composer);
+               view = e_html_editor_get_view (editor);
                data = g_byte_array_new ();
-               text = gtkhtml_editor_get_text_plain (editor, &length);
-               g_byte_array_append (data, (guint8 *) text, (guint) length);
+               text = e_html_editor_view_get_text_plain (view);
+               g_byte_array_append (data, (guint8 *) text, strlen (text));
                g_free (text);
 
                type = camel_content_type_new ("text", "plain");
@@ -1252,6 +1284,7 @@ composer_build_message (EMsgComposer *composer,
                }
        }
 
+ wrap_drafts_html:
        mem_stream = camel_stream_mem_new_with_byte_array (data);
        stream = camel_stream_filter_new (mem_stream);
        g_object_unref (mem_stream);
@@ -1298,25 +1331,27 @@ composer_build_message (EMsgComposer *composer,
         *        ...
         */
 
-       if (flags & COMPOSER_FLAG_HTML_CONTENT) {
+       if ((flags & COMPOSER_FLAG_HTML_CONTENT) != 0 &&
+           !(flags & COMPOSER_FLAG_SAVE_DRAFT)) {
                gchar *text;
+               guint count;
                gsize length;
                gboolean pre_encode;
+               EHTMLEditor *editor;
+               EHTMLEditorView *view;
+               GList *inline_images;
 
-               clear_current_images (composer);
-
-               if (flags & COMPOSER_FLAG_SAVE_OBJECT_DATA)
-                       gtkhtml_editor_run_command (editor, "save-data-on");
+               editor = e_msg_composer_get_editor (composer);
+               view = e_html_editor_get_view (editor);
+               inline_images = e_html_editor_view_get_parts_for_inline_images (view);
 
                data = g_byte_array_new ();
-               text = gtkhtml_editor_get_text_html (editor, &length);
+               text = e_html_editor_view_get_text_html (view);
+               length = strlen (text);
                g_byte_array_append (data, (guint8 *) text, (guint) length);
                pre_encode = text_requires_quoted_printable (text, length);
                g_free (text);
 
-               if (flags & COMPOSER_FLAG_SAVE_OBJECT_DATA)
-                       gtkhtml_editor_run_command (editor, "save-data-off");
-
                mem_stream = camel_stream_mem_new_with_byte_array (data);
                stream = camel_stream_filter_new (mem_stream);
                g_object_unref (mem_stream);
@@ -1366,7 +1401,9 @@ composer_build_message (EMsgComposer *composer,
 
                /* If there are inlined images, construct a multipart/related
                 * containing the multipart/alternative and the images. */
-               if (priv->current_images) {
+               count = g_list_length (inline_images);
+               if (count > 0) {
+                       guint ii;
                        CamelMultipart *html_with_images;
 
                        html_with_images = camel_multipart_new ();
@@ -1385,8 +1422,12 @@ composer_build_message (EMsgComposer *composer,
 
                        g_object_unref (body);
 
-                       add_inlined_images (composer, html_with_images);
-                       clear_current_images (composer);
+                       for (ii = 0; ii < count; ii++) {
+                               CamelMimePart *part = g_list_nth_data (inline_images, ii);
+                               camel_multipart_add_part (
+                                       html_with_images, part);
+                               g_object_unref (part);
+                       }
 
                        context->top_level_part =
                                CAMEL_DATA_WRAPPER (html_with_images);
@@ -1493,7 +1534,8 @@ use_top_signature (EMsgComposer *composer)
        return top_signature;
 }
 
-#define NO_SIGNATURE_TEXT \
+/* FIXME WEBKIT Nope....*/
+#define NO_SIGNATURE_TEXT      \
        "<!--+GtkHTML:<DATA class=\"ClueFlow\" " \
        "                     key=\"signature\" " \
        "                   value=\"1\">-->" \
@@ -1506,7 +1548,8 @@ set_editor_text (EMsgComposer *composer,
                  const gchar *text,
                  gboolean set_signature)
 {
-       gchar *body = NULL;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
        g_return_if_fail (text != NULL);
@@ -1527,20 +1570,23 @@ set_editor_text (EMsgComposer *composer,
 
        /* "Edit as New Message" sets "priv->is_from_message".
         * Always put the signature at the bottom for that case. */
+
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+
        if (!composer->priv->is_from_message && use_top_signature (composer)) {
+               gchar *body;
                /* put marker to the top */
-               body = g_strdup_printf ("<BR>" NO_SIGNATURE_TEXT "%s", text);
+               body = g_strdup_printf ("<BR>%s", text);
+               e_html_editor_view_set_text_html (view, body);
+               g_free (body);
        } else {
-               /* no marker => to the bottom */
-               body = g_strdup_printf ("%s<BR>", text);
+               e_html_editor_view_set_text_html (view, text);
        }
 
-       gtkhtml_editor_set_text_html (GTKHTML_EDITOR (composer), body, -1);
-
        if (set_signature)
                e_composer_update_signature (composer);
 
-       g_free (body);
 }
 
 /* Miscellaneous callbacks.  */
@@ -1548,12 +1594,16 @@ set_editor_text (EMsgComposer *composer,
 static void
 attachment_store_changed_cb (EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
        /* Mark the editor as changed so it prompts about unsaved
         * changes on close. */
-       editor = GTKHTML_EDITOR (composer);
-       gtkhtml_editor_set_changed (editor, TRUE);
+       editor = e_msg_composer_get_editor (composer);
+       if (editor) {
+               view = e_html_editor_get_view (editor);
+               e_html_editor_view_set_changed (view, TRUE);
+       }
 }
 
 static void
@@ -1648,11 +1698,11 @@ msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard,
                                          gint n_targets,
                                          EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
-       gboolean html_mode;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
-       editor = GTKHTML_EDITOR (composer);
-       html_mode = gtkhtml_editor_get_html_mode (editor);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
 
        /* Order is important here to ensure common use cases are
         * handled correctly.  See GNOME bug #603715 for details. */
@@ -1663,7 +1713,7 @@ msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard,
        }
 
        /* Only paste HTML content in HTML mode. */
-       if (html_mode) {
+       if (e_html_editor_view_get_html_mode (view)) {
                if (e_targets_include_html (targets, n_targets)) {
                        e_composer_paste_html (composer, clipboard);
                        return;
@@ -1682,48 +1732,31 @@ msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard,
 }
 
 static void
-msg_composer_paste_clipboard_cb (EWebViewGtkHTML *web_view,
-                                 EMsgComposer *composer)
+msg_composer_paste_primary_clipboard_cb (EHTMLEditorView *view,
+                                         EMsgComposer *composer)
 {
        GtkClipboard *clipboard;
 
-       clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+       clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
 
        gtk_clipboard_request_targets (
                clipboard, (GtkClipboardTargetsReceivedFunc)
                msg_composer_paste_clipboard_targets_cb, composer);
-
-       g_signal_stop_emission_by_name (web_view, "paste-clipboard");
 }
 
 static void
-msg_composer_realize_gtkhtml_cb (GtkWidget *widget,
+msg_composer_paste_clipboard_cb (EHTMLEditorView *view,
                                  EMsgComposer *composer)
 {
-       EAttachmentView *view;
-       GtkTargetList *target_list;
-       GtkTargetEntry *targets;
-       gint n_targets;
-
-       /* XXX GtkHTML doesn't set itself up as a drag destination until
-        *     it's realized, and we need to amend to its target list so
-        *     it will accept the same drag targets as the attachment bar.
-        *     Do this any earlier and GtkHTML will just overwrite us. */
-
-       /* When redirecting a message, the message body is not
-        * editable and therefore cannot be a drag destination. */
-       if (!e_web_view_gtkhtml_get_editable (E_WEB_VIEW_GTKHTML (widget)))
-               return;
-
-       view = e_msg_composer_get_attachment_view (composer);
+       GtkClipboard *clipboard;
 
-       target_list = e_attachment_view_get_target_list (view);
-       targets = gtk_target_table_new_from_list (target_list, &n_targets);
+       clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 
-       target_list = gtk_drag_dest_get_target_list (widget);
-       gtk_target_list_add_table (target_list, targets, n_targets);
+       gtk_clipboard_request_targets (
+               clipboard, (GtkClipboardTargetsReceivedFunc)
+               msg_composer_paste_clipboard_targets_cb, composer);
 
-       gtk_target_table_free (targets, n_targets);
+       g_signal_stop_emission_by_name (view, "paste-clipboard");
 }
 
 static gboolean
@@ -1744,6 +1777,31 @@ msg_composer_drag_motion_cb (GtkWidget *widget,
        return e_attachment_view_drag_motion (view, context, x, y, time);
 }
 
+static gchar *
+next_uri (guchar **uri_list,
+          gint *len,
+          gint *list_len)
+{
+       guchar *uri, *begin;
+
+       begin = *uri_list;
+       *len = 0;
+       while (**uri_list && **uri_list != '\n' && **uri_list != '\r' && *list_len) {
+               (*uri_list) ++;
+               (*len) ++;
+               (*list_len) --;
+       }
+
+       uri = (guchar *) g_strndup ((gchar *) begin, *len);
+
+       while ((!**uri_list || **uri_list == '\n' || **uri_list == '\r') && *list_len) {
+               (*uri_list) ++;
+               (*list_len) --;
+       }
+
+       return (gchar *) uri;
+}
+
 static void
 msg_composer_drag_data_received_cb (GtkWidget *widget,
                                     GdkDragContext *context,
@@ -1755,43 +1813,87 @@ msg_composer_drag_data_received_cb (GtkWidget *widget,
                                     EMsgComposer *composer)
 {
        EAttachmentView *view;
+       EHTMLEditor *editor;
+       EHTMLEditorView *html_editor_view;
+       EHTMLEditorSelection *editor_selection;
 
-       /* HTML mode has a few special cases for drops... */
-       if (gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer))) {
+       editor = e_msg_composer_get_editor (composer);
+       html_editor_view = e_html_editor_get_view (editor);
+       editor_selection = e_html_editor_view_get_selection (html_editor_view);
 
+       /* HTML mode has a few special cases for drops... */
+       if (e_html_editor_view_get_html_mode (html_editor_view)) {
                /* If we're receiving an image, we want the image to be
                 * inserted in the message body.  Let GtkHtml handle it. */
+               /* FIXME WebKit - how to reproduce this?
                if (gtk_selection_data_targets_include_image (selection, TRUE))
                        return;
-
+                */
                /* If we're receiving URIs and -all- the URIs point to
                 * image files, we want the image(s) to be inserted in
-                * the message body.  Let GtkHtml handle it. */
-               if (e_composer_selection_is_image_uris (composer, selection))
-                       return;
-       }
+                * the message body. */
+               if (e_composer_selection_is_image_uris (composer, selection)) {
+                       const guchar *data;
+                       gint length;
+                       gint list_len, len;
+                       gchar *uri;
+
+                       data = gtk_selection_data_get_data (selection);
+                       length = gtk_selection_data_get_length (selection);
+
+                       if (!data || length < 0)
+                               return;
+
+                       list_len = length;
+                       do {
+                               uri = next_uri ((guchar **) &data, &len, &list_len);
+                               e_html_editor_selection_insert_image (editor_selection, uri);
+                       } while (list_len);
+               }
 
-       view = e_msg_composer_get_attachment_view (composer);
+               if (e_composer_selection_is_base64_uris (composer, selection)) {
+                       const guchar *data;
+                       gint length;
+                       gint list_len, len;
+                       gchar *uri;
 
-       /* Forward the data to the attachment view.  Note that calling
-        * e_attachment_view_drag_data_received() will not work because
-        * that function only handles the case where all the other drag
-        * handlers have failed. */
-       e_attachment_paned_drag_data_received (
-               E_ATTACHMENT_PANED (view),
-               context, x, y, selection, info, time);
+                       data = gtk_selection_data_get_data (selection);
+                       length = gtk_selection_data_get_length (selection);
 
-       /* Stop the signal from propagating to GtkHtml. */
+                       if (!data || length < 0)
+                               return;
+
+                       list_len = length;
+                       do {
+                               uri = next_uri ((guchar **) &data, &len, &list_len);
+
+                               e_html_editor_selection_insert_image (editor_selection, uri);
+                       } while (list_len);
+               }
+       } else {
+               view = e_msg_composer_get_attachment_view (composer);
+
+               /* Forward the data to the attachment view.  Note that calling
+                * e_attachment_view_drag_data_received() will not work because
+                * that function only handles the case where all the other drag
+                * handlers have failed. */
+               e_attachment_paned_drag_data_received (
+                       E_ATTACHMENT_PANED (view),
+                       context, x, y, selection, info, time);
+       }
+       /* Stop the signal from propagating */
        g_signal_stop_emission_by_name (widget, "drag-data-received");
 }
 
 static void
 msg_composer_notify_header_cb (EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
-       editor = GTKHTML_EDITOR (composer);
-       gtkhtml_editor_set_changed (editor, TRUE);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_set_changed (view, TRUE);
 }
 
 static gboolean
@@ -1892,6 +1994,12 @@ msg_composer_get_property (GObject *object,
                            GParamSpec *pspec)
 {
        switch (property_id) {
+               case PROP_BUSY:
+                       g_value_set_boolean (
+                               value, e_msg_composer_is_busy (
+                               E_MSG_COMPOSER (object)));
+                       return;
+
                case PROP_FOCUS_TRACKER:
                        g_value_set_object (
                                value, e_msg_composer_get_focus_tracker (
@@ -1951,30 +2059,71 @@ msg_composer_gallery_drag_data_get (GtkIconView *icon_view,
 }
 
 static void
+composer_notify_activity_cb (EActivityBar *activity_bar,
+                             GParamSpec *pspec,
+                             EMsgComposer *composer)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       WebKitWebView *web_view;
+       gboolean editable;
+       gboolean busy;
+
+       busy = (e_activity_bar_get_activity (activity_bar) != NULL);
+
+       if (busy == composer->priv->busy)
+               return;
+
+       composer->priv->busy = busy;
+
+       if (busy)
+               e_msg_composer_save_focused_widget (composer);
+
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       web_view = WEBKIT_WEB_VIEW (view);
+
+       if (busy) {
+               editable = webkit_web_view_get_editable (web_view);
+               webkit_web_view_set_editable (web_view, FALSE);
+               composer->priv->saved_editable = editable;
+       } else {
+               editable = composer->priv->saved_editable;
+               webkit_web_view_set_editable (web_view, editable);
+       }
+
+       g_object_notify (G_OBJECT (composer), "busy");
+
+       if (!busy)
+               e_msg_composer_restore_focus_on_composer (composer);
+}
+
+static void
 msg_composer_constructed (GObject *object)
 {
        EShell *shell;
-       GtkhtmlEditor *editor;
        EMsgComposer *composer;
+       EActivityBar *activity_bar;
        EAttachmentView *view;
        EAttachmentStore *store;
        EComposerHeaderTable *table;
-       EWebViewGtkHTML *web_view;
+       EHTMLEditor *editor;
+       EHTMLEditorView *html_editor_view;
        GtkUIManager *ui_manager;
        GtkToggleAction *action;
        GSettings *settings;
        const gchar *id;
        gboolean active;
 
-       editor = GTKHTML_EDITOR (object);
        composer = E_MSG_COMPOSER (object);
 
        shell = e_msg_composer_get_shell (composer);
 
        e_composer_private_constructed (composer);
 
-       web_view = e_msg_composer_get_web_view (composer);
-       ui_manager = gtkhtml_editor_get_ui_manager (editor);
+       editor = e_msg_composer_get_editor (composer);
+       html_editor_view = e_html_editor_get_view (editor);
+       ui_manager = e_html_editor_get_ui_manager (editor);
        view = e_msg_composer_get_attachment_view (composer);
        table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table);
 
@@ -2004,6 +2153,12 @@ msg_composer_constructed (GObject *object)
                "/org/gnome/evolution/mail/composer-window/",
                E_RESTORE_WINDOW_SIZE);
 
+       activity_bar = e_html_editor_get_activity_bar (editor);
+       g_signal_connect (
+               activity_bar, "notify::activity",
+               G_CALLBACK (composer_notify_activity_cb), composer);
+
+
        /* Honor User Preferences */
 
        /* FIXME This should be an EMsgComposer property. */
@@ -2016,21 +2171,21 @@ msg_composer_constructed (GObject *object)
        /* Clipboard Support */
 
        g_signal_connect (
-               web_view, "paste-clipboard",
+               html_editor_view, "paste-clipboard",
                G_CALLBACK (msg_composer_paste_clipboard_cb), composer);
 
-       /* Drag-and-Drop Support */
-
        g_signal_connect (
-               web_view, "realize",
-               G_CALLBACK (msg_composer_realize_gtkhtml_cb), composer);
+               html_editor_view, "paste-primary-clipboard",
+               G_CALLBACK (msg_composer_paste_primary_clipboard_cb), composer);
+
+       /* Drag-and-Drop Support */
 
        g_signal_connect (
-               web_view, "drag-motion",
+               html_editor_view, "drag-motion",
                G_CALLBACK (msg_composer_drag_motion_cb), composer);
 
        g_signal_connect (
-               web_view, "drag-data-received",
+               html_editor_view, "drag-data-received",
                G_CALLBACK (msg_composer_drag_data_received_cb), composer);
 
        g_signal_connect (
@@ -2079,7 +2234,13 @@ msg_composer_constructed (GObject *object)
                G_CALLBACK (attachment_store_changed_cb), composer);
 
        /* Initialization may have tripped the "changed" state. */
-       gtkhtml_editor_set_changed (editor, FALSE);
+       e_html_editor_view_set_changed (html_editor_view, FALSE);
+
+       gtk_drag_dest_set (
+               GTK_WIDGET (html_editor_view),
+               GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
+               drag_dest_targets, G_N_ELEMENTS (drag_dest_targets),
+               GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
 
        id = "org.gnome.evolution.composer";
        e_plugin_ui_register_manager (ui_manager, id, composer);
@@ -2087,6 +2248,7 @@ msg_composer_constructed (GObject *object)
 
        e_extensible_load_extensions (E_EXTENSIBLE (composer));
 
+       e_msg_composer_set_body_text (composer, "", TRUE);
        /* Chain up to parent's constructed() method. */
        G_OBJECT_CLASS (e_msg_composer_parent_class)->constructed (object);
 }
@@ -2119,14 +2281,19 @@ msg_composer_dispose (GObject *object)
 static void
 msg_composer_map (GtkWidget *widget)
 {
+       EMsgComposer *composer;
        EComposerHeaderTable *table;
        GtkWidget *input_widget;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        const gchar *text;
 
        /* Chain up to parent's map() method. */
        GTK_WIDGET_CLASS (e_msg_composer_parent_class)->map (widget);
 
-       table = e_msg_composer_get_header_table (E_MSG_COMPOSER (widget));
+       composer = E_MSG_COMPOSER (widget);
+       editor = e_msg_composer_get_editor (composer);
+       table = e_msg_composer_get_header_table (composer);
 
        /* If the 'To' field is empty, focus it. */
        input_widget =
@@ -2149,7 +2316,8 @@ msg_composer_map (GtkWidget *widget)
        }
 
        /* Jump to the editor as a last resort. */
-       gtkhtml_editor_run_command (GTKHTML_EDITOR (widget), "grab-focus");
+       view = e_html_editor_get_view (editor);
+       gtk_widget_grab_focus (GTK_WIDGET (view));
 }
 
 static gboolean
@@ -2158,12 +2326,12 @@ msg_composer_key_press_event (GtkWidget *widget,
 {
        EMsgComposer *composer;
        GtkWidget *input_widget;
-       GtkhtmlEditor *editor;
-       EWebViewGtkHTML *web_view;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
-       editor = GTKHTML_EDITOR (widget);
        composer = E_MSG_COMPOSER (widget);
-       web_view = e_msg_composer_get_web_view (composer);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
 
        input_widget =
                e_composer_header_table_get_header (
@@ -2183,12 +2351,12 @@ msg_composer_key_press_event (GtkWidget *widget,
        }
 
        if (event->keyval == GDK_KEY_Tab && gtk_widget_is_focus (input_widget)) {
-               gtkhtml_editor_run_command (editor, "grab-focus");
+               gtk_widget_grab_focus (GTK_WIDGET (view));
                return TRUE;
        }
 
        if (event->keyval == GDK_KEY_ISO_Left_Tab &&
-               gtk_widget_is_focus (GTK_WIDGET (web_view))) {
+               gtk_widget_is_focus (GTK_WIDGET (view))) {
                gtk_widget_grab_focus (input_widget);
                return TRUE;
        }
@@ -2198,160 +2366,6 @@ msg_composer_key_press_event (GtkWidget *widget,
                key_press_event (widget, event);
 }
 
-static void
-msg_composer_cut_clipboard (GtkhtmlEditor *editor)
-{
-       /* Do nothing.  EFocusTracker handles this. */
-}
-
-static void
-msg_composer_copy_clipboard (GtkhtmlEditor *editor)
-{
-       /* Do nothing.  EFocusTracker handles this. */
-}
-
-static void
-msg_composer_paste_clipboard (GtkhtmlEditor *editor)
-{
-       /* Do nothing.  EFocusTracker handles this. */
-}
-
-static void
-msg_composer_select_all (GtkhtmlEditor *editor)
-{
-       /* Do nothing.  EFocusTracker handles this. */
-}
-
-static void
-msg_composer_command_before (GtkhtmlEditor *editor,
-                             const gchar *command)
-{
-       EMsgComposer *composer;
-       const gchar *data;
-
-       composer = E_MSG_COMPOSER (editor);
-
-       if (strcmp (command, "insert-paragraph") != 0)
-               return;
-
-       if (composer->priv->in_signature_insert)
-               return;
-
-       data = gtkhtml_editor_get_paragraph_data (editor, "orig");
-       if (data != NULL && *data == '1') {
-               gtkhtml_editor_run_command (editor, "text-default-color");
-               gtkhtml_editor_run_command (editor, "italic-off");
-               return;
-       };
-
-       data = gtkhtml_editor_get_paragraph_data (editor, "signature");
-       if (data != NULL && *data == '1') {
-               gtkhtml_editor_run_command (editor, "text-default-color");
-               gtkhtml_editor_run_command (editor, "italic-off");
-       }
-}
-
-static void
-msg_composer_command_after (GtkhtmlEditor *editor,
-                            const gchar *command)
-{
-       EMsgComposer *composer;
-       const gchar *data;
-
-       composer = E_MSG_COMPOSER (editor);
-
-       if (strcmp (command, "insert-paragraph") != 0)
-               return;
-
-       if (composer->priv->in_signature_insert)
-               return;
-
-       gtkhtml_editor_run_command (editor, "italic-off");
-
-       data = gtkhtml_editor_get_paragraph_data (editor, "orig");
-       if (data != NULL && *data == '1')
-               e_msg_composer_reply_indent (composer);
-       gtkhtml_editor_set_paragraph_data (editor, "orig", "0");
-
-       data = gtkhtml_editor_get_paragraph_data (editor, "signature");
-       if (data == NULL || *data != '1')
-               return;
-
-       /* Clear the signature. */
-       if (gtkhtml_editor_is_paragraph_empty (editor))
-               gtkhtml_editor_set_paragraph_data (editor, "signature" ,"0");
-
-       else if (gtkhtml_editor_is_previous_paragraph_empty (editor) &&
-               gtkhtml_editor_run_command (editor, "cursor-backward")) {
-
-               gtkhtml_editor_set_paragraph_data (editor, "signature", "0");
-               gtkhtml_editor_run_command (editor, "cursor-forward");
-       }
-
-       gtkhtml_editor_run_command (editor, "text-default-color");
-       gtkhtml_editor_run_command (editor, "italic-off");
-}
-
-static gchar *
-msg_composer_image_uri (GtkhtmlEditor *editor,
-                        const gchar *uri)
-{
-       EMsgComposer *composer;
-       GHashTable *hash_table;
-       CamelMimePart *part;
-       const gchar *cid;
-
-       composer = E_MSG_COMPOSER (editor);
-
-       hash_table = composer->priv->inline_images_by_url;
-       part = g_hash_table_lookup (hash_table, uri);
-
-       if (part == NULL && g_str_has_prefix (uri, "file:"))
-               part = e_msg_composer_add_inline_image_from_file (
-                       composer, uri + 5);
-
-       if (part == NULL && g_str_has_prefix (uri, "cid:")) {
-               hash_table = composer->priv->inline_images;
-               part = g_hash_table_lookup (hash_table, uri);
-       }
-
-       if (part == NULL)
-               return NULL;
-
-       composer->priv->current_images =
-               g_list_prepend (composer->priv->current_images, part);
-
-       cid = camel_mime_part_get_content_id (part);
-       if (cid == NULL)
-               return NULL;
-
-       return g_strconcat ("cid:", cid, NULL);
-}
-
-static void
-msg_composer_object_deleted (GtkhtmlEditor *editor)
-{
-       const gchar *data;
-
-       if (!gtkhtml_editor_is_paragraph_empty (editor))
-               return;
-
-       data = gtkhtml_editor_get_paragraph_data (editor, "orig");
-       if (data != NULL && *data == '1') {
-               gtkhtml_editor_set_paragraph_data (editor, "orig", "0");
-               gtkhtml_editor_run_command (editor, "indent-zero");
-               gtkhtml_editor_run_command (editor, "style-normal");
-               gtkhtml_editor_run_command (editor, "text-default-color");
-               gtkhtml_editor_run_command (editor, "italic-off");
-               gtkhtml_editor_run_command (editor, "insert-paragraph");
-               gtkhtml_editor_run_command (editor, "delete-back");
-       }
-
-       data = gtkhtml_editor_get_paragraph_data (editor, "signature");
-       if (data != NULL && *data == '1')
-               gtkhtml_editor_set_paragraph_data (editor, "signature", "0");
-}
-
 static gboolean
 msg_composer_presend (EMsgComposer *composer)
 {
@@ -2359,34 +2373,6 @@ msg_composer_presend (EMsgComposer *composer)
        return TRUE;
 }
 
-static void
-msg_composer_submit_alert (EAlertSink *alert_sink,
-                           EAlert *alert)
-{
-       EMsgComposerPrivate *priv;
-       EAlertBar *alert_bar;
-       GtkWidget *dialog;
-       GtkWindow *parent;
-
-       priv = E_MSG_COMPOSER_GET_PRIVATE (alert_sink);
-
-       switch (e_alert_get_message_type (alert)) {
-               case GTK_MESSAGE_INFO:
-               case GTK_MESSAGE_WARNING:
-               case GTK_MESSAGE_ERROR:
-                       alert_bar = E_ALERT_BAR (priv->alert_bar);
-                       e_alert_bar_add_alert (alert_bar, alert);
-                       break;
-
-               default:
-                       parent = GTK_WINDOW (alert_sink);
-                       dialog = e_alert_dialog_new (parent, alert);
-                       gtk_dialog_run (GTK_DIALOG (dialog));
-                       gtk_widget_destroy (dialog);
-                       break;
-       }
-}
-
 static gboolean
 msg_composer_accumulator_false_abort (GSignalInvocationHint *ihint,
                                       GValue *return_accu,
@@ -2402,12 +2388,27 @@ msg_composer_accumulator_false_abort (GSignalInvocationHint *ihint,
        return v_boolean;
 }
 
+/**
+ * e_msg_composer_is_busy:
+ * @composer: an #EMsgComposer
+ *
+ * Returns %TRUE only while an #EActivity is in progress.
+ *
+ * Returns: whether @composer is busy
+ **/
+gboolean
+e_msg_composer_is_busy (EMsgComposer *composer)
+{
+       g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
+
+       return composer->priv->busy;
+}
+
 static void
 e_msg_composer_class_init (EMsgComposerClass *class)
 {
        GObjectClass *object_class;
        GtkWidgetClass *widget_class;
-       GtkhtmlEditorClass *editor_class;
 
        g_type_class_add_private (class, sizeof (EMsgComposerPrivate));
 
@@ -2422,21 +2423,21 @@ e_msg_composer_class_init (EMsgComposerClass *class)
        widget_class->map = msg_composer_map;
        widget_class->key_press_event = msg_composer_key_press_event;
 
-       editor_class = GTKHTML_EDITOR_CLASS (class);
-       editor_class->cut_clipboard = msg_composer_cut_clipboard;
-       editor_class->copy_clipboard = msg_composer_copy_clipboard;
-       editor_class->paste_clipboard = msg_composer_paste_clipboard;
-       editor_class->select_all = msg_composer_select_all;
-       editor_class->command_before = msg_composer_command_before;
-       editor_class->command_after = msg_composer_command_after;
-       editor_class->image_uri = msg_composer_image_uri;
-       editor_class->link_clicked = NULL; /* EWebView handles this */
-       editor_class->object_deleted = msg_composer_object_deleted;
-
        class->presend = msg_composer_presend;
 
        g_object_class_install_property (
                object_class,
+               PROP_BUSY,
+               g_param_spec_boolean (
+                       "busy",
+                       "Busy",
+                       "Whether an activity is in progress",
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
                PROP_FOCUS_TRACKER,
                g_param_spec_object (
                        "focus-tracker",
@@ -2512,15 +2513,11 @@ e_msg_composer_class_init (EMsgComposerClass *class)
 }
 
 static void
-e_msg_composer_alert_sink_init (EAlertSinkInterface *iface)
-{
-       iface->submit_alert = msg_composer_submit_alert;
-}
-
-static void
 e_msg_composer_init (EMsgComposer *composer)
 {
        composer->priv = E_MSG_COMPOSER_GET_PRIVATE (composer);
+
+       composer->priv->editor = g_object_ref_sink (e_html_editor_new ());
 }
 
 /**
@@ -2538,7 +2535,23 @@ e_msg_composer_new (EShell *shell)
 
        return g_object_new (
                E_TYPE_MSG_COMPOSER,
-               "html", e_web_view_gtkhtml_new (), "shell", shell, NULL);
+               "shell", shell, NULL);
+}
+
+/**
+ * e_msg_composer_get_editor:
+ * @composer: an #EMsgComposer
+ *
+ * Returns @composer's internal #EHTMLEditor instance.
+ *
+ * Returns: an #EHTMLEditor
+ **/
+EHTMLEditor *
+e_msg_composer_get_editor (EMsgComposer *composer)
+{
+       g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+
+       return composer->priv->editor;
 }
 
 EFocusTracker *
@@ -2581,12 +2594,16 @@ add_attachments_handle_mime_part (EMsgComposer *composer,
 {
        CamelContentType *content_type;
        CamelDataWrapper *wrapper;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
        if (!mime_part)
                return;
 
        content_type = camel_mime_part_get_content_type (mime_part);
        wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
 
        if (CAMEL_IS_MULTIPART (wrapper)) {
                /* another layer of multipartness... */
@@ -2596,10 +2613,10 @@ add_attachments_handle_mime_part (EMsgComposer *composer,
        } else if (just_inlines) {
                if (camel_mime_part_get_content_id (mime_part) ||
                    camel_mime_part_get_content_location (mime_part))
-                       e_msg_composer_add_inline_image_from_mime_part (
-                               composer, mime_part);
+                       e_html_editor_view_add_inline_image_from_mime_part (
+                               view, mime_part);
        } else if (related && camel_content_type_is (content_type, "image", "*")) {
-               e_msg_composer_add_inline_image_from_mime_part (composer, mime_part);
+               e_html_editor_view_add_inline_image_from_mime_part (view, mime_part);
        } else if (camel_content_type_is (content_type, "text", "*") &&
                camel_mime_part_get_filename (mime_part) == NULL) {
                /* Do nothing if this is a text/anything without a
@@ -2988,16 +3005,40 @@ handle_multipart (EMsgComposer *composer,
 
                        /* 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);
-                       if (html)
-                               e_msg_composer_set_pending_body (composer, html, length);
+
+                       /* If we are opening message from Drafts */
+                       if (composer->priv->is_from_draft) {
+                               /* Extract the body */
+                               CamelDataWrapper *dw;
+
+                               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);
+
+                                       g_object_unref (mem);
+                               }
+                       } else {
+                               html = emcu_part_to_html (
+                                       composer, mime_part, &length, keep_signature, cancellable);
+                       }
+                       e_msg_composer_set_pending_body (composer, html, length);
 
                } else if (camel_mime_part_get_content_id (mime_part) ||
                           camel_mime_part_get_content_location (mime_part)) {
                        /* special in-line attachment */
-                       e_msg_composer_add_inline_image_from_mime_part (
-                               composer, mime_part);
+                       EHTMLEditor *editor;
+
+                       editor = e_msg_composer_get_editor (composer);
+                       e_html_editor_view_add_inline_image_from_mime_part (
+                               e_html_editor_get_view (editor), mime_part);
 
                } else {
                        /* normal attachment */
@@ -3009,28 +3050,46 @@ handle_multipart (EMsgComposer *composer,
 static void
 set_signature_gui (EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *nodes;
        EComposerHeaderTable *table;
        EMailSignatureComboBox *combo_box;
-       const gchar *data;
        gchar *uid;
+       gulong ii, length;
 
-       editor = GTKHTML_EDITOR (composer);
        table = e_msg_composer_get_header_table (composer);
        combo_box = e_composer_header_table_get_signature_combo_box (table);
 
-       if (!gtkhtml_editor_search_by_data (editor, 1, "ClueFlow", "signature", "1"))
-               return;
-
-       data = gtkhtml_editor_get_paragraph_data (editor, "signature_name");
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
 
-       if (!g_str_has_prefix (data, "uid:"))
-               return;
+       uid = NULL;
+       nodes = webkit_dom_document_get_elements_by_class_name (
+               document, "-x-evo-signature");
+       length = webkit_dom_node_list_get_length (nodes);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+               gchar *id;
+
+               node = webkit_dom_node_list_item (nodes, ii);
+               id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (node));
+               if (id && (strlen (id) == 1) && (*id == '1')) {
+                       uid = webkit_dom_element_get_attribute (
+                               WEBKIT_DOM_ELEMENT (node), "name");
+                       g_free (id);
+                       break;
+               }
+               g_free (id);
+       }
 
        /* The combo box active ID is the signature's ESource UID. */
-       uid = e_composer_decode_clue_value (data + 4);
-       gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), uid);
-       g_free (uid);
+       if (uid != NULL) {
+               gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), uid);
+               g_free (uid);
+       }
 }
 
 static void
@@ -3100,7 +3159,7 @@ e_msg_composer_new_with_message (EShell *shell,
 {
        CamelInternetAddress *to, *cc, *bcc;
        GList *To = NULL, *Cc = NULL, *Bcc = NULL, *postto = NULL;
-       const gchar *format, *subject;
+       const gchar *format, *subject, *composer_mode;
        EDestination **Tov, **Ccv, **Bccv;
        GHashTable *auto_cc, *auto_bcc;
        CamelContentType *content_type;
@@ -3110,6 +3169,8 @@ e_msg_composer_new_with_message (EShell *shell,
        EMsgComposerPrivate *priv;
        EComposerHeaderTable *table;
        ESource *source = NULL;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        GtkToggleAction *action;
        struct _camel_header_raw *xev;
        gchar *identity_uid;
@@ -3131,7 +3192,9 @@ e_msg_composer_new_with_message (EShell *shell,
 
        composer = e_msg_composer_new (shell);
        priv = E_MSG_COMPOSER_GET_PRIVATE (composer);
+       editor = e_msg_composer_get_editor (composer);
        table = e_msg_composer_get_header_table (composer);
+       view = e_html_editor_get_view (editor);
 
        if (postto) {
                e_composer_header_table_set_post_to_list (table, postto);
@@ -3250,6 +3313,13 @@ e_msg_composer_new_with_message (EShell *shell,
        /* Restore the format editing preference */
        format = camel_medium_get_header (
                CAMEL_MEDIUM (message), "X-Evolution-Format");
+
+       composer_mode = camel_medium_get_header (
+               CAMEL_MEDIUM (message), "X-Evolution-Composer-Mode");
+
+       if (composer_mode && *composer_mode)
+               composer->priv->is_from_draft = TRUE;
+
        if (format != NULL) {
                gchar **flags;
 
@@ -3259,11 +3329,21 @@ e_msg_composer_new_with_message (EShell *shell,
                flags = g_strsplit (format, ", ", 0);
                for (i = 0; flags[i]; i++) {
                        if (g_ascii_strcasecmp (flags[i], "text/html") == 0) {
-                               gtkhtml_editor_set_html_mode (
-                                       GTKHTML_EDITOR (composer), TRUE);
+                               if (g_ascii_strcasecmp (composer_mode, "text/html") == 0) {
+                                       e_html_editor_view_set_html_mode (
+                                               view, TRUE);
+                               } else {
+                                       e_html_editor_view_set_html_mode (
+                                               view, FALSE);
+                               }
                        } else if (g_ascii_strcasecmp (flags[i], "text/plain") == 0) {
-                               gtkhtml_editor_set_html_mode (
-                                       GTKHTML_EDITOR (composer), FALSE);
+                               if (g_ascii_strcasecmp (composer_mode, "text/html") == 0) {
+                                       e_html_editor_view_set_html_mode (
+                                               view, TRUE);
+                               } else {
+                                       e_html_editor_view_set_html_mode (
+                                               view, FALSE);
+                               }
                        } else if (g_ascii_strcasecmp (flags[i], "pgp-sign") == 0) {
                                action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
                                gtk_toggle_action_set_active (action, TRUE);
@@ -3367,14 +3447,35 @@ e_msg_composer_new_with_message (EShell *shell,
                                ACTION (SMIME_ENCRYPT)), TRUE);
                }
 
-               html = emcu_part_to_html (
-                       composer, CAMEL_MIME_PART (message),
-                       &length, keep_signature, cancellable);
-               if (html)
-                       e_msg_composer_set_pending_body (composer, html, length);
+               /* If we are opening message from Drafts */
+               if (composer->priv->is_from_draft) {
+                       /* Extract the body */
+                       CamelDataWrapper *dw;
+
+                       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);
+
+                               g_object_unref (mem);
+                       }
+               } else {
+                       html = emcu_part_to_html (
+                               composer, CAMEL_MIME_PART (message),
+                               &length, keep_signature, cancellable);
+               }
+               e_msg_composer_set_pending_body (composer, html, length);
        }
 
        priv->is_from_message = TRUE;
+       priv->set_signature_from_message = TRUE;
 
        /* We wait until now to set the body text because we need to
         * ensure that the attachment bar has all the attachments before
@@ -3403,7 +3504,8 @@ e_msg_composer_new_redirect (EShell *shell,
 {
        EMsgComposer *composer;
        EComposerHeaderTable *table;
-       EWebViewGtkHTML *web_view;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        const gchar *subject;
 
        g_return_val_if_fail (E_IS_SHELL (shell), NULL);
@@ -3421,8 +3523,9 @@ e_msg_composer_new_redirect (EShell *shell,
        e_composer_header_table_set_identity_uid (table, identity_uid);
        e_composer_header_table_set_subject (table, subject);
 
-       web_view = e_msg_composer_get_web_view (composer);
-       e_web_view_gtkhtml_set_editable (web_view, FALSE);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), FALSE);
 
        return composer;
 }
@@ -3473,30 +3576,6 @@ e_msg_composer_get_shell (EMsgComposer *composer)
        return E_SHELL (composer->priv->shell);
 }
 
-/**
- * e_msg_composer_get_web_view:
- * @composer: an #EMsgComposer
- *
- * Returns the #EWebView widget in @composer.
- *
- * Returns: the #EWebView
- **/
-EWebViewGtkHTML *
-e_msg_composer_get_web_view (EMsgComposer *composer)
-{
-       GtkHTML *html;
-       GtkhtmlEditor *editor;
-
-       g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
-
-       /* This is a convenience function to avoid
-        * repeating this awkwardness everywhere */
-       editor = GTKHTML_EDITOR (composer);
-       html = gtkhtml_editor_get_html (editor);
-
-       return E_WEB_VIEW_GTKHTML (html);
-}
-
 static void
 msg_composer_send_cb (EMsgComposer *composer,
                       GAsyncResult *result,
@@ -3504,7 +3583,8 @@ msg_composer_send_cb (EMsgComposer *composer,
 {
        CamelMimeMessage *message;
        EAlertSink *alert_sink;
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        GError *error = NULL;
 
        alert_sink = e_activity_get_alert_sink (context->activity);
@@ -3536,8 +3616,9 @@ msg_composer_send_cb (EMsgComposer *composer,
        g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
 
        /* The callback can set editor 'changed' if anything failed. */
-       editor = GTKHTML_EDITOR (composer);
-       gtkhtml_editor_set_changed (editor, FALSE);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_set_changed (view, TRUE);
 
        g_signal_emit (
                composer, signals[SEND], 0,
@@ -3557,9 +3638,8 @@ msg_composer_send_cb (EMsgComposer *composer,
 void
 e_msg_composer_send (EMsgComposer *composer)
 {
+       EHTMLEditor *editor;
        AsyncContext *context;
-       EAlertSink *alert_sink;
-       EActivityBar *activity_bar;
        GCancellable *cancellable;
        gboolean proceed_with_send = TRUE;
 
@@ -3573,18 +3653,12 @@ e_msg_composer_send (EMsgComposer *composer)
                return;
        }
 
-       context = g_slice_new0 (AsyncContext);
-       context->activity = e_composer_activity_new (composer);
-
-       alert_sink = E_ALERT_SINK (composer);
-       e_activity_set_alert_sink (context->activity, alert_sink);
+       editor = e_msg_composer_get_editor (composer);
 
-       cancellable = camel_operation_new ();
-       e_activity_set_cancellable (context->activity, cancellable);
-       g_object_unref (cancellable);
+       context = g_slice_new0 (AsyncContext);
+       context->activity = e_html_editor_new_activity (editor);
 
-       activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
-       e_activity_bar_set_activity (activity_bar, context->activity);
+       cancellable = e_activity_get_cancellable (context->activity);
 
        e_msg_composer_get_message (
                composer, G_PRIORITY_DEFAULT, cancellable,
@@ -3599,7 +3673,8 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer,
 {
        CamelMimeMessage *message;
        EAlertSink *alert_sink;
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        GError *error = NULL;
 
        alert_sink = e_activity_get_alert_sink (context->activity);
@@ -3640,8 +3715,9 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer,
        g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
 
        /* The callback can set editor 'changed' if anything failed. */
-       editor = GTKHTML_EDITOR (composer);
-       gtkhtml_editor_set_changed (editor, FALSE);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_set_changed (view, FALSE);
 
        g_signal_emit (
                composer, signals[SAVE_TO_DRAFTS],
@@ -3666,25 +3742,18 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer,
 void
 e_msg_composer_save_to_drafts (EMsgComposer *composer)
 {
+       EHTMLEditor *editor;
        AsyncContext *context;
-       EAlertSink *alert_sink;
-       EActivityBar *activity_bar;
        GCancellable *cancellable;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
-       context = g_slice_new0 (AsyncContext);
-       context->activity = e_composer_activity_new (composer);
-
-       alert_sink = E_ALERT_SINK (composer);
-       e_activity_set_alert_sink (context->activity, alert_sink);
+       editor = e_msg_composer_get_editor (composer);
 
-       cancellable = camel_operation_new ();
-       e_activity_set_cancellable (context->activity, cancellable);
-       g_object_unref (cancellable);
+       context = g_slice_new0 (AsyncContext);
+       context->activity = e_html_editor_new_activity (editor);
 
-       activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
-       e_activity_bar_set_activity (activity_bar, context->activity);
+       cancellable = e_activity_get_cancellable (context->activity);
 
        e_msg_composer_get_message_draft (
                composer, G_PRIORITY_DEFAULT, cancellable,
@@ -3699,7 +3768,8 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer,
 {
        CamelMimeMessage *message;
        EAlertSink *alert_sink;
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        GError *error = NULL;
 
        alert_sink = e_activity_get_alert_sink (context->activity);
@@ -3734,9 +3804,9 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer,
 
        async_context_free (context);
 
-       /* XXX This should be elsewhere. */
-       editor = GTKHTML_EDITOR (composer);
-       gtkhtml_editor_set_changed (editor, FALSE);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_set_changed (view, FALSE);
 }
 
 /**
@@ -3748,9 +3818,8 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer,
 void
 e_msg_composer_save_to_outbox (EMsgComposer *composer)
 {
+       EHTMLEditor *editor;
        AsyncContext *context;
-       EAlertSink *alert_sink;
-       EActivityBar *activity_bar;
        GCancellable *cancellable;
        gboolean proceed_with_save = TRUE;
 
@@ -3762,18 +3831,12 @@ e_msg_composer_save_to_outbox (EMsgComposer *composer)
        if (!proceed_with_save)
                return;
 
-       context = g_slice_new0 (AsyncContext);
-       context->activity = e_composer_activity_new (composer);
+       editor = e_msg_composer_get_editor (composer);
 
-       alert_sink = E_ALERT_SINK (composer);
-       e_activity_set_alert_sink (context->activity, alert_sink);
-
-       cancellable = camel_operation_new ();
-       e_activity_set_cancellable (context->activity, cancellable);
-       g_object_unref (cancellable);
+       context = g_slice_new0 (AsyncContext);
+       context->activity = e_html_editor_new_activity (editor);
 
-       activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
-       e_activity_bar_set_activity (activity_bar, context->activity);
+       cancellable = e_activity_get_cancellable (context->activity);
 
        e_msg_composer_get_message (
                composer, G_PRIORITY_DEFAULT, cancellable,
@@ -3835,26 +3898,19 @@ void
 e_msg_composer_print (EMsgComposer *composer,
                       GtkPrintOperationAction print_action)
 {
+       EHTMLEditor *editor;
        AsyncContext *context;
-       EAlertSink *alert_sink;
-       EActivityBar *activity_bar;
        GCancellable *cancellable;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
+       editor = e_msg_composer_get_editor (composer);
+
        context = g_slice_new0 (AsyncContext);
-       context->activity = e_composer_activity_new (composer);
+       context->activity = e_html_editor_new_activity (editor);
        context->print_action = print_action;
 
-       alert_sink = E_ALERT_SINK (composer);
-       e_activity_set_alert_sink (context->activity, alert_sink);
-
-       cancellable = camel_operation_new ();
-       e_activity_set_cancellable (context->activity, cancellable);
-       g_object_unref (cancellable);
-
-       activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
-       e_activity_bar_set_activity (activity_bar, context->activity);
+       cancellable = e_activity_get_cancellable (context->activity);
 
        e_msg_composer_get_message_print (
                composer, G_PRIORITY_DEFAULT, cancellable,
@@ -4226,15 +4282,21 @@ e_msg_composer_set_body (EMsgComposer *composer,
 {
        EMsgComposerPrivate *priv = composer->priv;
        EComposerHeaderTable *table;
-       EWebViewGtkHTML *web_view;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        ESource *source;
        const gchar *identity_uid;
        gchar *buff;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
        table = e_msg_composer_get_header_table (composer);
 
+       /* Disable signature */
+       priv->disable_signature = TRUE;
+
        identity_uid = e_composer_header_table_get_identity_uid (table);
        source = e_composer_header_table_ref_source (table, identity_uid);
 
@@ -4245,10 +4307,8 @@ e_msg_composer_set_body (EMsgComposer *composer,
        set_editor_text (composer, buff, FALSE);
        g_free (buff);
 
-       gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (composer), FALSE);
-
-       web_view = e_msg_composer_get_web_view (composer);
-       e_web_view_gtkhtml_set_editable (web_view, FALSE);
+       e_html_editor_view_set_html_mode (view, FALSE);
+       webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), FALSE);
 
        g_free (priv->mime_body);
        priv->mime_body = g_strdup (body);
@@ -4459,109 +4519,12 @@ e_msg_composer_attach (EMsgComposer *composer,
        attachment = e_attachment_new ();
        e_attachment_set_mime_part (attachment, mime_part);
        e_attachment_store_add_attachment (store, attachment);
-       e_attachment_load (attachment, NULL);
+       e_attachment_load_async (
+               attachment, (GAsyncReadyCallback)
+               e_attachment_load_handle_error, composer);
        g_object_unref (attachment);
 }
 
-/**
- * e_msg_composer_add_inline_image_from_file:
- * @composer: a composer object
- * @filename: the name of the file containing the image
- *
- * This reads in the image in @filename and adds it to @composer
- * as an inline image, to be wrapped in a multipart/related.
- *
- * Returns: the newly-created CamelMimePart (which must be reffed
- * if the caller wants to keep its own reference), or %NULL on error.
- **/
-CamelMimePart *
-e_msg_composer_add_inline_image_from_file (EMsgComposer *composer,
-                                           const gchar *filename)
-{
-       gchar *mime_type, *cid, *url, *name, *dec_file_name;
-       CamelStream *stream;
-       CamelDataWrapper *wrapper;
-       CamelMimePart *part;
-       EMsgComposerPrivate *p = composer->priv;
-
-       dec_file_name = g_strdup (filename);
-       camel_url_decode (dec_file_name);
-
-       if (!g_file_test (dec_file_name, G_FILE_TEST_IS_REGULAR))
-               return NULL;
-
-       stream = camel_stream_fs_new_with_name (
-               dec_file_name, O_RDONLY, 0, NULL);
-       if (!stream)
-               return NULL;
-
-       wrapper = camel_data_wrapper_new ();
-       camel_data_wrapper_construct_from_stream_sync (
-               wrapper, stream, NULL, NULL);
-       g_object_unref (CAMEL_OBJECT (stream));
-
-       mime_type = e_util_guess_mime_type (dec_file_name, TRUE);
-       if (mime_type == NULL)
-               mime_type = g_strdup ("application/octet-stream");
-       camel_data_wrapper_set_mime_type (wrapper, mime_type);
-       g_free (mime_type);
-
-       part = camel_mime_part_new ();
-       camel_medium_set_content (CAMEL_MEDIUM (part), wrapper);
-       g_object_unref (wrapper);
-
-       cid = camel_header_msgid_generate ();
-       camel_mime_part_set_content_id (part, cid);
-       name = g_path_get_basename (dec_file_name);
-       camel_mime_part_set_filename (part, name);
-       g_free (name);
-       camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
-
-       url = g_strdup_printf ("file:%s", dec_file_name);
-       g_hash_table_insert (p->inline_images_by_url, url, part);
-
-       url = g_strdup_printf ("cid:%s", cid);
-       g_hash_table_insert (p->inline_images, url, part);
-       g_free (cid);
-
-       g_free (dec_file_name);
-
-       return part;
-}
-
-/**
- * e_msg_composer_add_inline_image_from_mime_part:
- * @composer: a composer object
- * @part: a CamelMimePart containing image data
- *
- * This adds the mime part @part to @composer as an inline image, to
- * be wrapped in a multipart/related.
- **/
-void
-e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer,
-                                                CamelMimePart *part)
-{
-       gchar *url;
-       const gchar *location, *cid;
-       EMsgComposerPrivate *p = composer->priv;
-
-       cid = camel_mime_part_get_content_id (part);
-       if (!cid) {
-               camel_mime_part_set_content_id (part, NULL);
-               cid = camel_mime_part_get_content_id (part);
-       }
-
-       url = g_strdup_printf ("cid:%s", cid);
-       g_hash_table_insert (p->inline_images, url, part);
-       g_object_ref (part);
-
-       location = camel_mime_part_get_content_location (part);
-       if (location != NULL)
-               g_hash_table_insert (
-                       p->inline_images_by_url,
-                       g_strdup (location), part);
-}
-
 static void
 composer_get_message_ready (EMsgComposer *composer,
                             GAsyncResult *result,
@@ -4604,16 +4567,21 @@ e_msg_composer_get_message (EMsgComposer *composer,
        GSimpleAsyncResult *simple;
        GtkAction *action;
        ComposerFlags flags = 0;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+
        simple = g_simple_async_result_new (
                G_OBJECT (composer), callback,
                user_data, e_msg_composer_get_message);
 
        g_simple_async_result_set_check_cancellable (simple, cancellable);
 
-       if (gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer)))
+       if (e_html_editor_view_get_html_mode (view))
                flags |= COMPOSER_FLAG_HTML_CONTENT;
 
        action = ACTION (PRIORITIZE_MESSAGE);
@@ -4730,8 +4698,10 @@ e_msg_composer_get_message_draft (EMsgComposer *composer,
                                   GAsyncReadyCallback callback,
                                   gpointer user_data)
 {
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        GSimpleAsyncResult *simple;
-       ComposerFlags flags = COMPOSER_FLAG_DRAFT;
+       ComposerFlags flags = COMPOSER_FLAG_SAVE_DRAFT;
        GtkAction *action;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
@@ -4742,8 +4712,13 @@ e_msg_composer_get_message_draft (EMsgComposer *composer,
 
        g_simple_async_result_set_check_cancellable (simple, cancellable);
 
-       if (gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer)))
-               flags |= COMPOSER_FLAG_HTML_CONTENT;
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       /* We need to remember composer mode */
+       if (e_html_editor_view_get_html_mode (view))
+               flags |= COMPOSER_FLAG_HTML_MODE;
+       /* We want to save HTML content everytime when we save as draft */
+       flags |= COMPOSER_FLAG_SAVE_DRAFT;
 
        action = ACTION (PRIORITIZE_MESSAGE);
        if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
@@ -4872,17 +4847,19 @@ e_msg_composer_get_reply_to (EMsgComposer *composer)
 GByteArray *
 e_msg_composer_get_raw_message_text (EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        GByteArray *array;
        gchar *text;
-       gsize length;
 
        g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
 
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+
        array = g_byte_array_new ();
-       editor = GTKHTML_EDITOR (composer);
-       text = gtkhtml_editor_get_text_plain (editor, &length);
-       g_byte_array_append (array, (guint8 *) text, (guint) length);
+       text = e_html_editor_view_get_text_plain (view);
+       g_byte_array_append (array, (guint8 *) text, strlen (text));
        g_free (text);
 
        return array;
@@ -4915,22 +4892,24 @@ e_msg_composer_can_close (EMsgComposer *composer,
                           gboolean can_save_draft)
 {
        gboolean res = FALSE;
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        EComposerHeaderTable *table;
        GdkWindow *window;
        GtkWidget *widget;
        const gchar *subject, *message_name;
        gint response;
 
-       editor = GTKHTML_EDITOR (composer);
        widget = GTK_WIDGET (composer);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
 
        /* this means that there is an async operation running,
         * in which case the composer cannot be closed */
        if (!gtk_action_group_get_sensitive (composer->priv->async_actions))
                return FALSE;
 
-       if (!gtkhtml_editor_get_changed (editor))
+       if (!e_html_editor_view_get_changed (view))
                return TRUE;
 
        window = gtk_widget_get_window (widget);
@@ -4968,32 +4947,6 @@ e_msg_composer_can_close (EMsgComposer *composer,
        return res;
 }
 
-void
-e_msg_composer_reply_indent (EMsgComposer *composer)
-{
-       GtkhtmlEditor *editor;
-
-       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
-
-       editor = GTKHTML_EDITOR (composer);
-
-       if (!gtkhtml_editor_is_paragraph_empty (editor)) {
-               if (gtkhtml_editor_is_previous_paragraph_empty (editor))
-                       gtkhtml_editor_run_command (editor, "cursor-backward");
-               else {
-                       gtkhtml_editor_run_command (editor, "text-default-color");
-                       gtkhtml_editor_run_command (editor, "italic-off");
-                       gtkhtml_editor_run_command (editor, "insert-paragraph");
-                       return;
-               }
-       }
-
-       gtkhtml_editor_run_command (editor, "style-normal");
-       gtkhtml_editor_run_command (editor, "indent-zero");
-       gtkhtml_editor_run_command (editor, "text-default-color");
-       gtkhtml_editor_run_command (editor, "italic-off");
-}
-
 EComposerHeaderTable *
 e_msg_composer_get_header_table (EMsgComposer *composer)
 {
@@ -5010,76 +4963,101 @@ e_msg_composer_get_attachment_view (EMsgComposer *composer)
        return E_ATTACHMENT_VIEW (composer->priv->attachment_paned);
 }
 
-GList *
-e_load_spell_languages (void)
+void
+e_save_spell_languages (const GList *spell_dicts)
 {
        GSettings *settings;
-       GList *spell_languages = NULL;
-       gchar **strv;
-       gint ii;
+       GPtrArray *lang_array;
+
+       /* Build a list of spell check language codes. */
+       lang_array = g_ptr_array_new ();
+       while (spell_dicts != NULL) {
+               ESpellDictionary *dict = spell_dicts->data;
+               const gchar *language_code;
+
+               language_code = e_spell_dictionary_get_code (dict);
+               g_ptr_array_add (lang_array, (gpointer) language_code);
+
+               spell_dicts = g_list_next (spell_dicts);
+       }
+
+       g_ptr_array_add (lang_array, NULL);
 
-       /* Ask GSettings for a list of spell check language codes. */
+       /* Save the language codes to GSettings. */
        settings = g_settings_new ("org.gnome.evolution.mail");
-       strv = g_settings_get_strv (settings, "composer-spell-languages");
+       g_settings_set_strv (
+               settings, "composer-spell-languages",
+               (const gchar * const *) lang_array->pdata);
        g_object_unref (settings);
 
-       /* Convert the codes to spell language structs. */
-       for (ii = 0; strv[ii] != NULL; ii++) {
-               gchar *language_code = strv[ii];
-               const GtkhtmlSpellLanguage *language;
+       g_ptr_array_free (lang_array, TRUE);
+}
 
-               language = gtkhtml_spell_language_lookup (language_code);
-               if (language != NULL)
-                       spell_languages = g_list_prepend (
-                               spell_languages, (gpointer) language);
-       }
+void
+e_msg_composer_is_from_new_message (EMsgComposer *composer,
+                                    gboolean is_from_new_message)
+{
+       g_return_if_fail (composer != NULL);
+
+       composer->priv->is_from_new_message = is_from_new_message;
+}
+
+void
+e_msg_composer_save_focused_widget (EMsgComposer *composer)
+{
+       GtkWidget *widget;
 
-       g_strfreev (strv);
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
-       spell_languages = g_list_reverse (spell_languages);
+       widget = gtk_window_get_focus (GTK_WINDOW (composer));
+       composer->priv->focused_entry = widget;
 
-       /* Pick a default spell language if it came back empty. */
-       if (spell_languages == NULL) {
-               const GtkhtmlSpellLanguage *language;
+       if (E_IS_HTML_EDITOR_VIEW (widget)) {
+               EHTMLEditorSelection *selection;
 
-               language = gtkhtml_spell_language_lookup (NULL);
+               selection = e_html_editor_view_get_selection (
+                       E_HTML_EDITOR_VIEW (widget));
 
-               if (language) {
-                       spell_languages = g_list_prepend (
-                               spell_languages, (gpointer) language);
-               }
+               e_html_editor_selection_save (selection);
        }
 
-       return spell_languages;
+       if (GTK_IS_EDITABLE (widget)) {
+               gtk_editable_get_selection_bounds (
+                       GTK_EDITABLE (widget),
+                       &composer->priv->focused_entry_selection_start,
+                       &composer->priv->focused_entry_selection_end);
+       }
 }
 
 void
-e_save_spell_languages (GList *spell_languages)
+e_msg_composer_restore_focus_on_composer (EMsgComposer *composer)
 {
-       GSettings *settings;
-       GPtrArray *lang_array;
+       GtkWidget *widget = composer->priv->focused_entry;
 
-       /* Build a list of spell check language codes. */
-       lang_array = g_ptr_array_new ();
-       while (spell_languages != NULL) {
-               const GtkhtmlSpellLanguage *language;
-               const gchar *language_code;
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
-               language = spell_languages->data;
-               language_code = gtkhtml_spell_language_get_code (language);
-               g_ptr_array_add (lang_array, (gpointer) language_code);
+       if (!widget)
+               return;
+
+       gtk_window_set_focus (GTK_WINDOW (composer), widget);
 
-               spell_languages = g_list_next (spell_languages);
+       if (GTK_IS_EDITABLE (widget)) {
+               gtk_editable_select_region (
+                       GTK_EDITABLE (widget),
+                       composer->priv->focused_entry_selection_start,
+                       composer->priv->focused_entry_selection_end);
        }
 
-       g_ptr_array_add (lang_array, NULL);
+       if (E_IS_HTML_EDITOR_VIEW (widget)) {
+               EHTMLEditorSelection *selection;
 
-       /* Save the language codes to GSettings. */
-       settings = g_settings_new ("org.gnome.evolution.mail");
-       g_settings_set_strv (
-               settings, "composer-spell-languages",
-               (const gchar * const *) lang_array->pdata);
-       g_object_unref (settings);
+               e_html_editor_view_force_spell_check (E_HTML_EDITOR_VIEW (widget));
 
-       g_ptr_array_free (lang_array, TRUE);
+               selection = e_html_editor_view_get_selection (
+                       E_HTML_EDITOR_VIEW (widget));
+
+               e_html_editor_selection_restore (selection);
+       }
+
+       composer->priv->focused_entry = NULL;
 }
diff --git a/composer/e-msg-composer.h b/composer/e-msg-composer.h
index 1fbc104..f699041 100644
--- a/composer/e-msg-composer.h
+++ b/composer/e-msg-composer.h
@@ -24,7 +24,6 @@
 #define E_MSG_COMPOSER_H
 
 #include <camel/camel.h>
-#include <gtkhtml-editor.h>
 #include <libebook/libebook.h>
 
 #include <shell/e-shell.h>
@@ -57,12 +56,12 @@ typedef struct _EMsgComposerClass EMsgComposerClass;
 typedef struct _EMsgComposerPrivate EMsgComposerPrivate;
 
 struct _EMsgComposer {
-       GtkhtmlEditor parent;
+       GtkWindow parent;
        EMsgComposerPrivate *priv;
 };
 
 struct _EMsgComposerClass {
-       GtkhtmlEditorClass parent_class;
+       GtkWindowClass parent_class;
 
        /* Signals */
        gboolean        (*presend)              (EMsgComposer *composer);
@@ -93,12 +92,11 @@ EMsgComposer *      e_msg_composer_new_redirect     (EShell *shell,
                                                 CamelMimeMessage *message,
                                                 const gchar *identity_uid,
                                                 GCancellable *cancellable);
+EHTMLEditor *  e_msg_composer_get_editor       (EMsgComposer *composer);
 EFocusTracker *        e_msg_composer_get_focus_tracker
                                                (EMsgComposer *composer);
 CamelSession * e_msg_composer_ref_session      (EMsgComposer *composer);
 EShell *       e_msg_composer_get_shell        (EMsgComposer *composer);
-EWebViewGtkHTML *
-               e_msg_composer_get_web_view     (EMsgComposer *composer);
 
 void           e_msg_composer_send             (EMsgComposer *composer);
 void           e_msg_composer_save_to_drafts   (EMsgComposer *composer);
@@ -131,12 +129,6 @@ void               e_msg_composer_set_source_headers
                                                 CamelMessageFlags flags);
 void           e_msg_composer_attach           (EMsgComposer *composer,
                                                 CamelMimePart *mime_part);
-CamelMimePart *        e_msg_composer_add_inline_image_from_file
-                                               (EMsgComposer *composer,
-                                                const gchar *filename);
-void           e_msg_composer_add_inline_image_from_mime_part
-                                               (EMsgComposer *composer,
-                                                CamelMimePart *part);
 void           e_msg_composer_get_message      (EMsgComposer *composer,
                                                 gint io_priority,
                                                 GCancellable *cancellable,
@@ -175,8 +167,6 @@ CamelInternetAddress *
 CamelInternetAddress *
                e_msg_composer_get_reply_to     (EMsgComposer *composer);
 
-void           e_msg_composer_clear_inlined_table
-                                               (EMsgComposer *composer);
 void           e_msg_composer_add_message_attachments
                                                (EMsgComposer *composer,
                                                 CamelMimeMessage *message,
@@ -186,8 +176,6 @@ void                e_msg_composer_request_close    (EMsgComposer *composer);
 gboolean       e_msg_composer_can_close        (EMsgComposer *composer,
                                                 gboolean can_save_draft);
 
-void           e_msg_composer_reply_indent     (EMsgComposer *composer);
-
 EComposerHeaderTable *
                e_msg_composer_get_header_table (EMsgComposer *composer);
 EAttachmentView *
@@ -198,9 +186,15 @@ GByteArray *       e_msg_composer_get_raw_message_text
 
 gboolean       e_msg_composer_is_exiting       (EMsgComposer *composer);
 
-GList *                e_load_spell_languages          (void);
-void           e_save_spell_languages          (GList *spell_languages);
-
+void           e_save_spell_languages          (const GList *spell_languages);
+void           e_msg_composer_is_from_new_message
+                                               (EMsgComposer *composer,
+                                                gboolean is_from_new_message);
+void           e_msg_composer_save_focused_widget
+                                               (EMsgComposer *composer);
+void           e_msg_composer_restore_focus_on_composer
+                                               (EMsgComposer *composer);
+gboolean       e_msg_composer_is_busy          (EMsgComposer *composer);
 G_END_DECLS
 
 #endif /* E_MSG_COMPOSER_H */
diff --git a/configure.ac b/configure.ac
index b03c1e6..fcd15e5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -48,9 +48,10 @@ m4_define([eds_minimum_version], [evo_version])
 m4_define([gtkhtml_minimum_version], [4.5.2])
 m4_define([gdk_pixbuf_minimum_version], [2.24.0])
 m4_define([gcr_minimum_version], [3.4])
+m4_define([enchant_minimum_version], [1.1.7])
 m4_define([gnome_desktop_minimum_version], [2.91.3])
 m4_define([gsettings_desktop_schemas_minimum_version], [2.91.92])
-m4_define([webkitgtk_minimum_version], [2.0.1])
+m4_define([webkitgtk_minimum_version], [2.2.0])
 m4_define([libgdata_minimum_version], [0.10])
 m4_define([libxml_minimum_version], [2.7.3])
 m4_define([shared_mime_info_minimum_version], [0.22])
@@ -286,6 +287,15 @@ PKG_CHECK_MODULES([EVOLUTION_DATA_SERVER],
 AC_SUBST(EVOLUTION_DATA_SERVER_CFLAGS)
 AC_SUBST(EVOLUTION_DATA_SERVER_LIBS)
 
+
+dnl ****************
+dnl Enchant Library
+dnl ****************
+PKG_CHECK_MODULES([ENCHANT],
+       [enchant >= enchant_minimum_version])
+AC_SUBST(ENCHANT_CFLAGS)
+AC_SUBST(ENCHANT_LIBS)
+
 dnl ******************************
 dnl Canberra / Canberra-GTK Sound
 dnl ******************************
@@ -482,6 +492,42 @@ fi
 AC_CHECK_FUNCS(mkdtemp)
 
 dnl **************************************************
+dnl iso-codes
+dnl **************************************************
+AC_MSG_CHECKING([for iso-codes package])
+have_iso_codes=no
+if $PKG_CONFIG --exists iso-codes; then
+       if $PKG_CONFIG iso-codes --atleast-version=0.49; then
+               have_iso_codes=yes
+               AC_MSG_RESULT([$have_iso_codes])
+       else
+               AC_MSG_WARN([iso-codes detected, but version 0.49 or later is required due to licensing])
+       fi
+else
+       AC_MSG_RESULT([$have_iso_codes])
+fi
+
+if test "x$have_iso_codes" = "xyes"; then
+       AC_MSG_CHECKING([whether iso-codes has iso-639 and iso-3166 domains])
+       if $PKG_CONFIG --variable=domains iso-codes | grep 639 >/dev/null 2>&1 && \
+               $PKG_CONFIG --variable=domains iso-codes | grep 3166 >/dev/null 2>&1 ; then
+                       result=yes
+       else
+               result=no
+               have_iso_codes=no
+       fi
+       AC_MSG_RESULT([$result])
+fi
+
+if test "x$have_iso_codes" = "xyes"; then
+       AC_DEFINE_UNQUOTED([ISO_CODES_PREFIX],
+               ["`$PKG_CONFIG --variable=prefix iso-codes`"],
+               [ISO codes prefix])
+       AC_DEFINE([HAVE_ISO_CODES], [1],
+               [Define if you have the iso-codes package])
+fi
+
+dnl **************************************************
 dnl Accessibility support
 dnl **************************************************
 PKG_CHECK_MODULES([A11Y], [atk])
@@ -567,16 +613,6 @@ AM_CONDITIONAL(SUNLDAP, [test x$with_sunldap != xno])
 
 AC_DEFINE(HANDLE_LIBICAL_MEMORY, 1, [Define it once memory returned by libical is free'ed properly])
 
-dnl *************************
-dnl GTKHTML check
-dnl XXX Drop the version from the package name?
-dnl *************************
-PKG_CHECK_MODULES([GTKHTML],
-       [libgtkhtml-4.0 >= gtkhtml_minimum_version
-        gtkhtml-editor-4.0])
-AC_SUBST(GTKHTML_CFLAGS)
-AC_SUBST(GTKHTML_LIBS)
-
 dnl ********************************************************************************
 dnl security extension support (SSL and S/MIME)
 dnl
diff --git a/data/evolution.convert b/data/evolution.convert
index 60ff3bb..d25e6f4 100644
--- a/data/evolution.convert
+++ b/data/evolution.convert
@@ -128,7 +128,6 @@ composer-show-post-from = /apps/evolution/mail/composer/show_post_from
 composer-show-post-reply-to = /apps/evolution/mail/composer/show_post_reply_to
 composer-show-reply-to = /apps/evolution/mail/composer/show_mail_reply_to
 composer-sign-reply-if-signed = /apps/evolution/mail/composer/sign_reply_if_signed
-composer-spell-color = /apps/evolution/mail/composer/spell_color
 composer-spell-languages = /apps/evolution/mail/composer/spell_languages
 composer-top-signature = /apps/evolution/mail/composer/top_signature
 default-account = /apps/evolution/mail/default_account
diff --git a/data/org.gnome.evolution.mail.gschema.xml.in b/data/org.gnome.evolution.mail.gschema.xml.in
index c1b2e25..c13540d 100644
--- a/data/org.gnome.evolution.mail.gschema.xml.in
+++ b/data/org.gnome.evolution.mail.gschema.xml.in
@@ -95,11 +95,6 @@
       <_summary>Send HTML mail by default</_summary>
       <_description>Send HTML mail by default.</_description>
     </key>
-    <key name="composer-spell-color" type="s">
-      <default>'#FFFF00000000'</default>
-      <_summary>Spell checking color</_summary>
-      <_description>Underline color for misspelled words when using inline spelling.</_description>
-    </key>
     <key name="composer-spell-languages" type="as">
       <default>[]</default>
       <_summary>Spell checking languages</_summary>
@@ -165,6 +160,16 @@
       <_summary>List of localized 'Re'</_summary>
       <_description>Comma-separated list of localized 'Re' abbreviations to skip in a subject text when 
replying to a message, as an addition to the standard "Re" prefix. An example is 'SV,AV'.</_description>
     </key>
+    <key name="composer-developer-mode" type="b">
+      <default>false</default>
+      <_summary>Enable developer mode</_summary>
+      <_description>Enables some hidden actions and tools aimed for development and debugging.</_description>
+    </key>
+    <key name="composer-word-wrap-length" type="i">
+      <default>71</default>
+      <_summary>Number of characters for wrapping</_summary>
+      <_description>Will autowrap lines after given number of characters.</_description>
+    </key>
     <key name="drag-and-drop-save-file-format" type="s">
       <default>'mbox'</default>
       <_summary>Save file format for drag-and-drop operation</_summary>
diff --git a/doc/reference/evolution-mail-composer/Makefile.am 
b/doc/reference/evolution-mail-composer/Makefile.am
index 580dc63..3fbc28f 100644
--- a/doc/reference/evolution-mail-composer/Makefile.am
+++ b/doc/reference/evolution-mail-composer/Makefile.am
@@ -22,14 +22,12 @@ GTKDOC_CFLAGS= \
        -I$(top_builddir)                                               \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                                 \
        $(GNOME_PLATFORM_CFLAGS)                                        \
-       $(GTKHTML_CFLAGS)                                               \
        $(NULL)
 
 GTKDOC_LIBS=                                                           \
        $(top_builddir)/composer/libevolution-mail-composer.la          \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)                                                 \
        $(NULL)
 
 # This includes the standard gtk-doc make rules, copied by gtkdocize.
diff --git a/doc/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml 
b/doc/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml
index becbfd6..b7d06b3 100644
--- a/doc/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml
+++ b/doc/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml
@@ -16,7 +16,6 @@
   <chapter>
     <title>Mail Composition</title>
     <xi:include href="xml/e-msg-composer.xml"/>
-    <xi:include href="xml/e-composer-activity.xml"/>
     <xi:include href="xml/e-composer-header-table.xml"/>
     <xi:include href="xml/e-composer-header.xml"/>
     <xi:include href="xml/e-composer-from-header.xml"/>
diff --git a/doc/reference/evolution-mail-composer/evolution-mail-composer-sections.txt 
b/doc/reference/evolution-mail-composer/evolution-mail-composer-sections.txt
index 4b286f9..cbe3f9e 100644
--- a/doc/reference/evolution-mail-composer/evolution-mail-composer-sections.txt
+++ b/doc/reference/evolution-mail-composer/evolution-mail-composer-sections.txt
@@ -1,23 +1,4 @@
 <SECTION>
-<FILE>e-composer-activity</FILE>
-<TITLE>EComposerActivity</TITLE>
-EComposerActivity
-e_composer_activity_new
-e_composer_activity_get_composer
-<SUBSECTION Standard>
-E_COMPOSER_ACTIVITY
-E_IS_COMPOSER_ACTIVITY
-E_TYPE_COMPOSER_ACTIVITY
-E_COMPOSER_ACTIVITY_CLASS
-E_IS_COMPOSER_ACTIVITY_CLASS
-E_COMPOSER_ACTIVITY_GET_CLASS
-EComposerActivityClass
-e_composer_activity_get_type
-<SUBSECTION Private>
-EComposerActivityPrivate
-</SECTION>
-
-<SECTION>
 <FILE>e-composer-from-header</FILE>
 <TITLE>EComposerFromHeader</TITLE>
 EComposerFromHeader
diff --git a/doc/reference/evolution-mail-composer/evolution-mail-composer.types 
b/doc/reference/evolution-mail-composer/evolution-mail-composer.types
index bf4ab7c..5e26731 100644
--- a/doc/reference/evolution-mail-composer/evolution-mail-composer.types
+++ b/doc/reference/evolution-mail-composer/evolution-mail-composer.types
@@ -1,4 +1,3 @@
-#include <composer/e-composer-activity.h>
 #include <composer/e-composer-from-header.h>
 #include <composer/e-composer-header-table.h>
 #include <composer/e-composer-header.h>
@@ -8,7 +7,6 @@
 #include <composer/e-composer-text-header.h>
 #include <composer/e-msg-composer.h>
 
-e_composer_activity_get_type
 e_composer_from_header_get_type
 e_composer_header_get_type
 e_composer_header_table_get_type
diff --git a/doc/reference/evolution-mail-formatter/Makefile.am 
b/doc/reference/evolution-mail-formatter/Makefile.am
index aff3eb7..ed313fc 100644
--- a/doc/reference/evolution-mail-formatter/Makefile.am
+++ b/doc/reference/evolution-mail-formatter/Makefile.am
@@ -21,14 +21,12 @@ GTKDOC_CFLAGS= \
        -I$(top_builddir)                                               \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                                 \
        $(GNOME_PLATFORM_CFLAGS)                                        \
-       $(GTKHTML_CFLAGS)                                               \
        $(NULL)
 
 GTKDOC_LIBS=                                                           \
        $(top_builddir)/em-format/libevolution-mail-formatter.la        \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)                                                 \
        $(NULL)
 
 # This includes the standard gtk-doc make rules, copied by gtkdocize.
diff --git a/doc/reference/evolution-shell/Makefile.am b/doc/reference/evolution-shell/Makefile.am
index 7afbc72..c64068e 100644
--- a/doc/reference/evolution-shell/Makefile.am
+++ b/doc/reference/evolution-shell/Makefile.am
@@ -22,7 +22,6 @@ GTKDOC_CFLAGS= \
        -I$(top_builddir)                                               \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                                 \
        $(GNOME_PLATFORM_CFLAGS)                                        \
-       $(GTKHTML_CFLAGS)                                               \
        $(NULL)
 
 GTKDOC_LIBS=                                                           \
@@ -31,7 +30,6 @@ GTKDOC_LIBS=                                                          \
        $(top_builddir)/e-util/libevolution-util.la                     \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)                                                 \
        $(NULL)
 
 # This includes the standard gtk-doc make rules, copied by gtkdocize.
diff --git a/doc/reference/evolution-util/Makefile.am b/doc/reference/evolution-util/Makefile.am
index a7055a4..48d1d13 100644
--- a/doc/reference/evolution-util/Makefile.am
+++ b/doc/reference/evolution-util/Makefile.am
@@ -16,6 +16,9 @@ CFILE_GLOB = $(top_srcdir)/e-util/*.c
 
 # Ignore all accessiblity headers.
 IGNORE_HFILES = \
+       e-html-editor-actions.h \
+       e-html-editor-private.h \
+       e-html-editor-utils.h \
        e-marshal.h \
        e-table-col-dnd.h \
        e-table-defines.h \
@@ -52,14 +55,12 @@ GTKDOC_CFLAGS  =                                    \
        -I$(top_builddir)                               \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
        $(NULL)
 
 GTKDOC_LIBS =                                          \
        $(top_builddir)/e-util/libevolution-util.la     \
        $(EVOLUTION_DATA_SERVER_LIBS)                   \
        $(GNOME_PLATFORM_LIBS)                          \
-       $(GTKHTML_LIBS)                                 \
        $(NULL)
 
 # Extra options to supply to gtkdoc-mkdb
diff --git a/doc/reference/evolution-util/evolution-util-docs.sgml 
b/doc/reference/evolution-util/evolution-util-docs.sgml
index 53badcc..262ddae 100644
--- a/doc/reference/evolution-util/evolution-util-docs.sgml
+++ b/doc/reference/evolution-util/evolution-util-docs.sgml
@@ -94,12 +94,40 @@
     <title>HTML Rendering</title>
     <xi:include href="xml/e-web-view.xml"/>
     <xi:include href="xml/e-web-view-preview.xml"/>
-    <xi:include href="xml/e-web-view-gtkhtml.xml"/>
     <xi:include href="xml/e-file-request.xml"/>
     <xi:include href="xml/e-stock-request.xml"/>
   </chapter>
 
   <chapter>
+    <title>HTML Editing</title>
+    <xi:include href="xml/e-color-chooser-widget.xml"/>
+    <xi:include href="xml/e-color-combo.xml"/>
+    <xi:include href="xml/e-emoticon.xml"/>
+    <xi:include href="xml/e-emoticon-action.xml"/>
+    <xi:include href="xml/e-emoticon-chooser.xml"/>
+    <xi:include href="xml/e-emoticon-chooser-menu.xml"/>
+    <xi:include href="xml/e-emoticon-tool-button.xml"/>
+    <xi:include href="xml/e-html-editor.xml"/>
+    <xi:include href="xml/e-html-editor-cell-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-find-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-hrule-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-image-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-link-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-page-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-paragraph-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-replace-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-selection.xml"/>
+    <xi:include href="xml/e-html-editor-spell-check-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-table-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-text-dialog.xml"/>
+    <xi:include href="xml/e-html-editor-widget.xml"/>
+    <xi:include href="xml/e-image-chooser-dialog.xml"/>
+    <xi:include href="xml/e-spell-checker.xml"/>
+    <xi:include href="xml/e-spell-dictionary.xml"/>
+  </chapter>
+
+  <chapter>
     <title>Mail Signatures</title>
     <xi:include href="xml/e-mail-signature-combo-box.xml"/>
     <xi:include href="xml/e-mail-signature-editor.xml"/>
diff --git a/doc/reference/evolution-util/evolution-util-sections.txt 
b/doc/reference/evolution-util/evolution-util-sections.txt
index 5f7497c..b5b4ee7 100644
--- a/doc/reference/evolution-util/evolution-util-sections.txt
+++ b/doc/reference/evolution-util/evolution-util-sections.txt
@@ -1323,6 +1323,55 @@ EClientSelectorPrivate
 </SECTION>
 
 <SECTION>
+<FILE>e-color-chooser-widget</FILE>
+<TITLE>EColorChooserWidget</TITLE>
+EColorChooserWidget
+e_color_chooser_widget_new
+<SUBSECTION Standard>
+E_COLOR_CHOOSER_WIDGET
+E_IS_COLOR_CHOOSER_WIDGET
+E_TYPE_COLOR_CHOOSER_WIDGET
+E_COLOR_CHOOSER_WIDGET_CLASS
+E_IS_COLOR_CHOOSER_WIDGET_CLASS
+E_COLOR_CHOOSER_WIDGET_GET_CLASS
+EColorChooserWidgetClass
+e_color_chooser_widget_get_type
+<SUBSECTION Private>
+EColorChooserWidgetPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-color-combo</FILE>
+<TITLE>EColorCombo</TITLE>
+EColorCombo
+e_color_combo_new
+e_color_combo_new_defaults
+e_color_combo_popup
+e_color_combo_popdown
+e_color_combo_get_current_color
+e_color_combo_set_current_color
+e_color_combo_get_default_color
+e_color_combo_set_default_color
+e_color_combo_get_default_label
+e_color_combo_set_default_label
+e_color_combo_get_default_transparent
+e_color_combo_set_default_transparent
+e_color_combo_get_palette
+e_color_combo_set_palette
+<SUBSECTION Standard>
+E_COLOR_COMBO
+E_IS_COLOR_COMBO
+E_TYPE_COLOR_COMBO
+E_COLOR_COMBO_CLASS
+E_IS_COLOR_COMBO_CLASS
+E_COLOR_COMBO_GET_CLASS
+EColorComboClass
+e_color_combo_get_type
+<SUBSECTION Private>
+EColorComboPrivate
+</SECTION>
+
+<SECTION>
 <FILE>e-config</FILE>
 <TITLE>EConfig</TITLE>
 EConfig
@@ -1519,6 +1568,890 @@ e_dialog_button_new_with_icon
 </SECTION>
 
 <SECTION>
+<FILE>e-editor</FILE>
+<TITLE>EEditor</TITLE>
+EEditor
+e_editor_new
+e_editor_is_busy
+e_editor_get_editor_widget
+e_editor_get_builder
+e_editor_get_ui_manager
+e_editor_get_action
+e_editor_get_action_group
+e_editor_get_widget
+e_editor_get_managed_widget
+e_editor_get_filename
+e_editor_set_filename
+e_editor_new_activity
+e_editor_pack_above
+e_editor_save
+<SUBSECTION Standard>
+E_EDITOR
+E_IS_EDITOR
+E_TYPE_EDITOR
+E_EDITOR_CLASS
+E_IS_EDITOR_CLASS
+E_EDITOR_GET_CLASS
+EEditorClass
+e_editor_get_type
+<SUBSECTION Private>
+EEditorPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-cell-dialog</FILE>
+<TITLE>EHTMLEditorCellDialog</TITLE>
+EHTMLEditorCellDialog
+e_html_editor_cell_dialog_new
+e_html_editor_cell_dialog_show
+<SUBSECTION Standard>
+E_HTML_EDITOR_CELL_DIALOG
+E_IS_HTML_EDITOR_CELL_DIALOG
+E_TYPE_HTML_EDITOR_CELL_DIALOG
+E_HTML_EDITOR_CELL_DIALOG_CLASS
+E_IS_HTML_EDITOR_CELL_DIALOG_CLASS
+E_HTML_EDITOR_CELL_DIALOG_GET_CLASS
+EHTMLEditorCellDialogClass
+e_html_editor_cell_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorCellDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-dialog</FILE>
+<TITLE>EEditorDialog</TITLE>
+e_editor_dialog_get_editor
+e_editor_dialog_get_button_box
+e_editor_dialog_get_container
+<SUBSECTION Standard>
+E_EDITOR_DIALOG
+E_IS_EDITOR_DIALOG
+E_TYPE_EDITOR_DIALOG
+E_EDITOR_DIALOG_CLASS
+E_IS_EDITOR_DIALOG_CLASS
+E_EDITOR_DIALOG_GET_CLASS
+EEditorDialogClass
+e_editor_dialog_get_type
+<SUBSECTION Private>
+EEditorDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-find-dialog</FILE>
+<TITLE>EHTMLEditorFindDialog</TITLE>
+e_html_editor_find_dialog_new
+e_html_editor_find_dialog_find_next
+<SUBSECTION Standard>
+E_HTML_EDITOR_FIND_DIALOG
+E_IS_HTML_EDITOR_FIND_DIALOG
+E_TYPE_HTML_EDITOR_FIND_DIALOG
+E_HTML_EDITOR_FIND_DIALOG_CLASS
+E_IS_HTML_EDITOR_FIND_DIALOG_CLASS
+E_HTML_EDITOR_FIND_DIALOG_GET_CLASS
+EHTMLEditorFindDialogClass
+e_html_editor_find_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorFindDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-hrule-dialog</FILE>
+<TITLE>EHTMLEditorHRuleDialog</TITLE>
+EHTMLEditorHRuleDialog
+e_html_editor_hrule_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_HRULE_DIALOG
+E_IS_HTML_EDITOR_HRULE_DIALOG
+E_TYPE_HTML_EDITOR_HRULE_DIALOG
+E_HTML_EDITOR_HRULE_DIALOG_CLASS
+E_IS_HTML_EDITOR_HRULE_DIALOG_CLASS
+E_HTML_EDITOR_HRULE_DIALOG_GET_CLASS
+EHTMLEditorHRuleDialogClass
+e_html_editor_hrule_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorHRuleDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-image-dialog</FILE>
+<TITLE>EHTMLEditorImageDialog</TITLE>
+EHTMLEditorImageDialog
+e_html_editor_image_dialog_new
+e_html_editor_image_dialog_show
+<SUBSECTION Standard>
+E_HTML_EDITOR_IMAGE_DIALOG
+E_IS_HTML_EDITOR_IMAGE_DIALOG
+E_TYPE_HTML_EDITOR_IMAGE_DIALOG
+E_HTML_EDITOR_IMAGE_DIALOG_CLASS
+E_IS_HTML_EDITOR_IMAGE_DIALOG_CLASS
+E_HTML_EDITOR_IMAGE_DIALOG_GET_CLASS
+EHTMLEditorImageDialogClass
+e_html_editor_image_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorImageDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-link-dialog</FILE>
+<TITLE>EHTMLEditorLinkDialog</TITLE>
+EHTMLEditorLinkDialog
+e_html_editor_link_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_LINK_DIALOG
+E_IS_HTML_EDITOR_LINK_DIALOG
+E_TYPE_HTML_EDITOR_LINK_DIALOG
+E_HTML_EDITOR_LINK_DIALOG_CLASS
+E_IS_HTML_EDITOR_LINK_DIALOG_CLASS
+E_HTML_EDITOR_LINK_DIALOG_GET_CLASS
+EHTMLEditorLinkDialogClass
+e_html_editor_link_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorLinkDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-page-dialog</FILE>
+<TITLE>EHTMLEditorPageDialog</TITLE>
+EHTMLEditorPageDialog
+e_html_editor_page_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_PAGE_DIALOG
+E_IS_HTML_EDITOR_PAGE_DIALOG
+E_TYPE_HTML_EDITOR_PAGE_DIALOG
+E_HTML_EDITOR_PAGE_DIALOG_CLASS
+E_IS_HTML_EDITOR_PAGE_DIALOG_CLASS
+E_HTML_EDITOR_PAGE_DIALOG_GET_CLASS
+EHTMLEditorPageDialogClass
+e_html_editor_page_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorPageDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-paragraph-dialog</FILE>
+<TITLE>EHTMLEditorParagraphDialog</TITLE>
+EHTMLEditorParagraphDialog
+e_html_editor_paragraph_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_PARAGRAPH_DIALOG
+E_IS_HTML_EDITOR_PARAGRAPH_DIALOG
+E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG
+E_HTML_EDITOR_PARAGRAPH_DIALOG_CLASS
+E_IS_HTML_EDITOR_PARAGRAPH_DIALOG_CLASS
+E_HTML_EDITOR_PARAGRAPH_DIALOG_GET_CLASS
+EHTMLEditorParagraphDialogClass
+e_html_editor_paragraph_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorParagraphDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-replace-dialog</FILE>
+<TITLE>EHTMLEditorReplaceDialog</TITLE>
+EHTMLEditorReplaceDialog
+e_html_editor_replace_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_REPLACE_DIALOG
+E_IS_HTML_EDITOR_REPLACE_DIALOG
+E_TYPE_HTML_EDITOR_REPLACE_DIALOG
+E_HTML_EDITOR_REPLACE_DIALOG_CLASS
+E_IS_HTML_EDITOR_REPLACE_DIALOG_CLASS
+E_HTML_EDITOR_REPLACE_DIALOG_GET_CLASS
+EHTMLEditorReplaceDialogClass
+e_html_editor_replace_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorReplaceDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-selection</FILE>
+<TITLE>EEditorSelection</TITLE>
+e_editor_selection_ref_editor_widget
+e_editor_selection_has_text
+e_editor_selection_get_caret_word
+e_editor_selection_replace_caret_word
+EEditorSelectionAlignment
+e_editor_selection_get_alignment
+e_editor_selection_set_alignment
+e_editor_selection_get_background_color
+e_editor_selection_set_background_color
+e_editor_selection_get_font_color
+e_editor_selection_set_font_color
+e_editor_selection_get_font_name
+e_editor_selection_set_font_name
+EEditorSelectionFontSize
+e_editor_selection_get_font_size
+e_editor_selection_set_font_size
+EEditorSelectionBlockFormat
+e_editor_selection_get_block_format
+e_editor_selection_set_block_format
+e_editor_selection_is_citation
+e_editor_selection_is_indented
+e_editor_selection_indent
+e_editor_selection_unindent
+e_editor_selection_is_bold
+e_editor_selection_set_bold
+e_editor_selection_is_italic
+e_editor_selection_set_italic
+e_editor_selection_is_monospaced
+e_editor_selection_set_monospaced
+e_editor_selection_is_strike_through
+e_editor_selection_set_strike_through
+e_editor_selection_is_superscript
+e_editor_selection_set_superscript
+e_editor_selection_is_subscript
+e_editor_selection_set_subscript
+e_editor_selection_is_underline
+e_editor_selection_set_underline
+e_editor_selection_unlink
+e_editor_selection_create_link
+e_editor_selection_get_string
+e_editor_selection_replace
+e_editor_selection_insert_html
+e_editor_selection_insert_image
+e_editor_selection_insert_text
+e_editor_selection_wrap_lines
+e_editor_selection_save
+e_editor_selection_restore
+EEditorSelectionGranularity
+e_editor_selection_move
+e_editor_selection_extend
+<SUBSECTION Standard>
+E_EDITOR_SELECTION
+E_IS_EDITOR_SELECTION
+E_TYPE_EDITOR_SELECTION
+E_EDITOR_SELECTION_CLASS
+E_IS_EDITOR_SELECTION_CLASS
+E_EDITOR_SELECTION_GET_CLASS
+EEditorSelectionClass
+e_editor_selection_get_type
+<SUBSECTION Private>
+EEditorSelectionPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-spell-check-dialog</FILE>
+<TITLE>EHTMLEditorSpellCheckDialog</TITLE>
+EHTMLEditorSpellCheckDialog
+e_html_editor_spell_check_dialog_new
+e_html_editor_spell_check_dialog_update_dictionaries
+<SUBSECTION Standard>
+E_HTML_EDITOR_SPELL_CHECK_DIALOG
+E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG
+E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG
+E_HTML_EDITOR_SPELL_CHECK_DIALOG_CLASS
+E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG_CLASS
+E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_CLASS
+EHTMLEditorSpellCheckDialogClass
+e_html_editor_spell_check_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorSpellCheckDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-table-dialog</FILE>
+<TITLE>EHTMLEditorTableDialog</TITLE>
+EHTMLEditorTableDialog
+e_html_editor_table_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_TABLE_DIALOG
+E_IS_HTML_EDITOR_TABLE_DIALOG
+E_TYPE_HTML_EDITOR_TABLE_DIALOG
+E_HTML_EDITOR_TABLE_DIALOG_CLASS
+E_IS_HTML_EDITOR_TABLE_DIALOG_CLASS
+E_HTML_EDITOR_TABLE_DIALOG_GET_CLASS
+EHTMLEditorTableDialogClass
+e_html_editor_table_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorTableDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-text-dialog</FILE>
+<TITLE>EHTMLEditorTextDialog</TITLE>
+EHTMLEditorTextDialog
+e_html_editor_text_dialog_new
+<SUBSECTION Standard>
+E_HTML_EDITOR_TEXT_DIALOG
+E_IS_HTML_EDITOR_TEXT_DIALOG
+E_TYPE_HTML_EDITOR_TEXT_DIALOG
+E_HTML_EDITOR_TEXT_DIALOG_CLASS
+E_IS_HTML_EDITOR_TEXT_DIALOG_CLASS
+E_HTML_EDITOR_TEXT_DIALOG_GET_CLASS
+EHTMLEditorTextDialogClass
+e_html_editor_text_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorTextDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-widget</FILE>
+<TITLE>EEditorWidget</TITLE>
+EEditorWidget
+e_editor_widget_new
+e_editor_widget_get_selection
+EEditorWidgetCommand
+e_editor_widget_exec_command
+e_editor_widget_get_changed
+e_editor_widget_set_changed
+e_editor_widget_get_html_mode
+e_editor_widget_set_html_mode
+e_editor_widget_get_inline_spelling
+e_editor_widget_set_inline_spelling
+e_editor_widget_get_magic_links
+e_editor_widget_set_magic_links
+e_editor_widget_get_magic_smileys
+e_editor_widget_set_magic_smileys
+e_editor_widget_get_spell_checker
+e_editor_widget_get_text_html
+e_editor_widget_get_text_plain
+e_editor_widget_set_text_html
+e_editor_widget_set_text_plain
+e_editor_widget_paste_clipboard_quoted
+e_editor_widget_update_fonts
+<SUBSECTION Standard>
+E_EDITOR_WIDGET
+E_IS_EDITOR_WIDGET
+E_TYPE_EDITOR_WIDGET
+E_EDITOR_WIDGET_CLASS
+E_IS_EDITOR_WIDGET_CLASS
+E_EDITOR_WIDGET_GET_CLASS
+EEditorWidgetClass
+e_editor_widget_get_type
+<SUBSECTION Private>
+EEditorWidgetPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon</FILE>
+<TITLE>EEmoticon</TITLE>
+EEmoticon
+e_emoticon_equal
+e_emoticon_copy
+e_emoticon_free
+e_emoticon_get_uri
+<SUBSECTION Standard>
+E_TYPE_EMOTICON
+e_emoticon_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-action</FILE>
+<TITLE>EEmoticonAction</TITLE>
+EEmoticonAction
+e_emoticon_action_new
+<SUBSECTION Standard>
+E_EMOTICON_ACTION
+E_IS_EMOTICON_ACTION
+E_TYPE_EMOTICON_ACTION
+E_EMOTICON_ACTION_CLASS
+E_IS_EMOTICON_ACTION_CLASS
+E_EMOTICON_ACTION_GET_CLASS
+EEmoticonActionClass
+e_emoticon_action_get_type
+<SUBSECTION Private>
+EEmoticonActionPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-chooser</FILE>
+<TITLE>EEmoticonChooser</TITLE>
+EEmoticonChooser
+EEmoticonChooserInterface
+e_emoticon_chooser_get_current_emoticon
+e_emoticon_chooser_set_current_emoticon
+e_emoticon_chooser_item_activated
+e_emoticon_chooser_get_items
+e_emoticon_chooser_lookup_emoticon
+<SUBSECTION Standard>
+E_EMOTICON_CHOOSER
+E_IS_EMOTICON_CHOOSER
+E_TYPE_EMOTICON_CHOOSER
+E_EMOTICON_CHOOSER_GET_INTERFACE
+e_emoticon_chooser_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-chooser-menu</FILE>
+<TITLE>EEmoticonChooserMenu</TITLE>
+EEmoticonChooserMenu
+e_emoticon_chooser_menu_new
+<SUBSECTION Standard>
+E_EMOTICON_CHOOSER_MENU
+E_IS_EMOTICON_CHOOSER_MENU
+E_TYPE_EMOTICON_CHOOSER_MENU
+E_EMOTICON_CHOOSER_MENU_CLASS
+E_IS_EMOTICON_CHOOSER_MENU_CLASS
+E_EMOTICON_CHOOSER_MENU_GET_CLASS
+EEmoticonChooserMenuClass
+e_emoticon_chooser_menu_get_type
+<SUBSECTION Private>
+EEmoticonChooserMenuPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-tool-button</FILE>
+<TITLE>EEmoticonToolButton</TITLE>
+EEmoticonToolButton
+e_emoticon_tool_button_new
+e_emoticon_tool_button_popup
+e_emoticon_tool_button_popdown
+<SUBSECTION Standard>
+E_EMOTICON_TOOL_BUTTON
+E_IS_EMOTICON_TOOL_BUTTON
+E_TYPE_EMOTICON_TOOL_BUTTON
+E_EMOTICON_TOOL_BUTTON_CLASS
+E_IS_EMOTICON_TOOL_BUTTON_CLASS
+E_EMOTICON_TOOL_BUTTON_GET_CLASS
+EEmoticonToolButtonClass
+e_emoticon_tool_button_get_type
+<SUBSECTION Private>
+EEmoticonToolButtonPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor</FILE>
+<TITLE>EHTMLEditor</TITLE>
+EHTMLEditor
+e_html_editor_new
+e_html_editor_is_busy
+e_html_editor_get_view
+e_html_editor_get_builder
+e_html_editor_get_ui_manager
+e_html_editor_get_action
+e_html_editor_get_action_group
+e_html_editor_get_widget
+e_html_editor_get_managed_widget
+e_html_editor_get_filename
+e_html_editor_set_filename
+e_html_editor_new_activity
+e_html_editor_pack_above
+e_html_editor_save
+<SUBSECTION Standard>
+E_HTML_EDITOR
+E_IS_HTML_EDITOR
+E_TYPE_HTML_EDITOR
+E_HTML_EDITOR_CLASS
+E_IS_HTML_EDITOR_CLASS
+E_HTML_EDITOR_GET_CLASS
+EHTMLEditorClass
+e_html_editor_get_type
+<SUBSECTION Private>
+EHTMLEditorPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-cell-dialog</FILE>
+<TITLE>EEditorCellDialog</TITLE>
+EEditorCellDialog
+e_editor_cell_dialog_new
+e_editor_cell_dialog_show
+<SUBSECTION Standard>
+E_EDITOR_CELL_DIALOG
+E_IS_EDITOR_CELL_DIALOG
+E_TYPE_EDITOR_CELL_DIALOG
+E_EDITOR_CELL_DIALOG_CLASS
+E_IS_EDITOR_CELL_DIALOG_CLASS
+E_EDITOR_CELL_DIALOG_GET_CLASS
+EEditorCellDialogClass
+e_editor_cell_dialog_get_type
+<SUBSECTION Private>
+EEditorCellDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-dialog</FILE>
+<TITLE>EHTMLEditorDialog</TITLE>
+e_html_editor_dialog_get_editor
+e_html_editor_dialog_get_button_box
+e_html_editor_dialog_get_container
+<SUBSECTION Standard>
+E_HTML_EDITOR_DIALOG
+E_IS_HTML_EDITOR_DIALOG
+E_TYPE_HTML_EDITOR_DIALOG
+E_HTML_EDITOR_DIALOG_CLASS
+E_IS_HTML_EDITOR_DIALOG_CLASS
+E_HTML_EDITOR_DIALOG_GET_CLASS
+EEditorDialogClass
+e_html_editor_dialog_get_type
+<SUBSECTION Private>
+EHTMLEditorDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-find-dialog</FILE>
+<TITLE>EEditorFindDialog</TITLE>
+e_editor_find_dialog_new
+e_editor_find_dialog_find_next
+<SUBSECTION Standard>
+E_EDITOR_FIND_DIALOG
+E_IS_EDITOR_FIND_DIALOG
+E_TYPE_EDITOR_FIND_DIALOG
+E_EDITOR_FIND_DIALOG_CLASS
+E_IS_EDITOR_FIND_DIALOG_CLASS
+E_EDITOR_FIND_DIALOG_GET_CLASS
+EEditorFindDialogClass
+e_editor_find_dialog_get_type
+<SUBSECTION Private>
+EEditorFindDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-hrule-dialog</FILE>
+<TITLE>EEditorHRuleDialog</TITLE>
+EEditorHRuleDialog
+e_editor_hrule_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_HRULE_DIALOG
+E_IS_EDITOR_HRULE_DIALOG
+E_TYPE_EDITOR_HRULE_DIALOG
+E_EDITOR_HRULE_DIALOG_CLASS
+E_IS_EDITOR_HRULE_DIALOG_CLASS
+E_EDITOR_HRULE_DIALOG_GET_CLASS
+EEditorHRuleDialogClass
+e_editor_hrule_dialog_get_type
+<SUBSECTION Private>
+EEditorHRuleDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-image-dialog</FILE>
+<TITLE>EEditorImageDialog</TITLE>
+EEditorImageDialog
+e_editor_image_dialog_new
+e_editor_image_dialog_show
+<SUBSECTION Standard>
+E_EDITOR_IMAGE_DIALOG
+E_IS_EDITOR_IMAGE_DIALOG
+E_TYPE_EDITOR_IMAGE_DIALOG
+E_EDITOR_IMAGE_DIALOG_CLASS
+E_IS_EDITOR_IMAGE_DIALOG_CLASS
+E_EDITOR_IMAGE_DIALOG_GET_CLASS
+EEditorImageDialogClass
+e_editor_image_dialog_get_type
+<SUBSECTION Private>
+EEditorImageDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-link-dialog</FILE>
+<TITLE>EEditorLinkDialog</TITLE>
+EEditorLinkDialog
+e_editor_link_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_LINK_DIALOG
+E_IS_EDITOR_LINK_DIALOG
+E_TYPE_EDITOR_LINK_DIALOG
+E_EDITOR_LINK_DIALOG_CLASS
+E_IS_EDITOR_LINK_DIALOG_CLASS
+E_EDITOR_LINK_DIALOG_GET_CLASS
+EEditorLinkDialogClass
+e_editor_link_dialog_get_type
+<SUBSECTION Private>
+EEditorLinkDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-page-dialog</FILE>
+<TITLE>EEditorPageDialog</TITLE>
+EEditorPageDialog
+e_editor_page_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_PAGE_DIALOG
+E_IS_EDITOR_PAGE_DIALOG
+E_TYPE_EDITOR_PAGE_DIALOG
+E_EDITOR_PAGE_DIALOG_CLASS
+E_IS_EDITOR_PAGE_DIALOG_CLASS
+E_EDITOR_PAGE_DIALOG_GET_CLASS
+EEditorPageDialogClass
+e_editor_page_dialog_get_type
+<SUBSECTION Private>
+EEditorPageDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-paragraph-dialog</FILE>
+<TITLE>EEditorParagraphDialog</TITLE>
+EEditorParagraphDialog
+e_editor_paragraph_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_PARAGRAPH_DIALOG
+E_IS_EDITOR_PARAGRAPH_DIALOG
+E_TYPE_EDITOR_PARAGRAPH_DIALOG
+E_EDITOR_PARAGRAPH_DIALOG_CLASS
+E_IS_EDITOR_PARAGRAPH_DIALOG_CLASS
+E_EDITOR_PARAGRAPH_DIALOG_GET_CLASS
+EEditorParagraphClass
+e_editor_paragraph_dialog_get_type
+<SUBSECTION Private>
+EEditorParagraphDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-replace-dialog</FILE>
+<TITLE>EEditorReplaceDialog</TITLE>
+EEditorReplaceDialog
+e_editor_replace_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_REPLACE_DIALOG
+E_IS_EDITOR_REPLACE_DIALOG
+E_TYPE_EDITOR_REPLACE_DIALOG
+E_EDITOR_REPLACE_DIALOG_CLASS
+E_IS_EDITOR_REPLACE_DIALOG_CLASS
+E_EDITOR_REPLACE_DIALOG_GET_CLASS
+EEditorReplaceDialogClass
+e_editor_replace_dialog_get_type
+<SUBSECTION Private>
+EEditorReplaceDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-html-editor-selection</FILE>
+<TITLE>EHTMLEditorSelection</TITLE>
+e_html_editor_selection_ref_html_html_editor_view
+e_html_editor_selection_has_text
+e_html_editor_selection_get_caret_word
+e_html_editor_selection_replace_caret_word
+EHTMLEditorSelectionAlignment
+e_html_editor_selection_get_alignment
+e_html_editor_selection_set_alignment
+e_html_editor_selection_get_background_color
+e_html_editor_selection_set_background_color
+e_html_editor_selection_get_font_color
+e_html_editor_selection_set_font_color
+e_html_editor_selection_get_font_name
+e_html_editor_selection_set_font_name
+EHTMLEditorSelectionFontSize
+e_html_editor_selection_get_font_size
+e_html_editor_selection_set_font_size
+EHTMLEditorSelectionBlockFormat
+e_html_editor_selection_get_block_format
+e_html_editor_selection_set_block_format
+e_html_editor_selection_is_citation
+e_html_editor_selection_is_indented
+e_html_editor_selection_indent
+e_html_editor_selection_unindent
+e_html_editor_selection_is_bold
+e_html_editor_selection_set_bold
+e_html_editor_selection_is_italic
+e_html_editor_selection_set_italic
+e_html_editor_selection_is_monospaced
+e_html_editor_selection_set_monospaced
+e_html_editor_selection_is_strike_through
+e_html_editor_selection_set_strike_through
+e_html_editor_selection_is_superscript
+e_html_editor_selection_set_superscript
+e_html_editor_selection_is_subscript
+e_html_editor_selection_set_subscript
+e_html_editor_selection_is_underline
+e_html_editor_selection_set_underline
+e_html_editor_selection_unlink
+e_html_editor_selection_create_link
+e_html_editor_selection_get_string
+e_html_editor_selection_replace
+e_html_editor_selection_insert_html
+e_html_editor_selection_insert_image
+e_html_editor_selection_insert_text
+e_html_editor_selection_wrap_lines
+e_html_editor_selection_save
+e_html_editor_selection_restore
+EHTMLEditorSelectionGranularity
+e_html_editor_selection_move
+e_html_editor_selection_extend
+<SUBSECTION Standard>
+E_HTML_EDITOR_SELECTION
+E_IS_HTML_EDITOR_SELECTION
+E_TYPE_HTML_EDITOR_SELECTION
+E_HTML_EDITOR_SELECTION_CLASS
+E_IS_HTML_EDITOR_SELECTION_CLASS
+E_HTML_EDITOR_SELECTION_GET_CLASS
+EHTMLEditorSelectionClass
+e_html_editor_selection_get_type
+<SUBSECTION Private>
+EHTMLEditorSelectionPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-spell-check-dialog</FILE>
+<TITLE>EEditorSpellCheckDialog</TITLE>
+EEditorSpellCheckDialog
+e_editor_spell_check_dialog_new
+e_editor_spell_check_dialog_update_dictionaries
+<SUBSECTION Standard>
+E_EDITOR_SPELL_CHECK_DIALOG
+E_IS_EDITOR_SPELL_CHECK_DIALOG
+E_TYPE_EDITOR_SPELL_CHECK_DIALOG
+E_EDITOR_SPELL_CHECK_DIALOG_CLASS
+E_IS_EDITOR_SPELL_CHECK_DIALOG_CLASS
+E_EDITOR_SPELL_CHECK_DIALOG_GET_CLASS
+EEditorSpellCheckDialogClass
+e_editor_spell_check_dialog_get_type
+<SUBSECTION Private>
+EEditorSpellCheckDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-table-dialog</FILE>
+<TITLE>EEditorTableDialog</TITLE>
+EEditorTableDialog
+e_editor_table_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_TABLE_DIALOG
+E_IS_EDITOR_TABLE_DIALOG
+E_TYPE_EDITOR_TABLE_DIALOG
+E_EDITOR_TABLE_DIALOG_CLASS
+E_IS_EDITOR_TABLE_DIALOG_CLASS
+E_EDITOR_TABLE_DIALOG_GET_CLASS
+EEditorTableDialogClass
+e_editor_table_dialog_get_type
+<SUBSECTION Private>
+EEditorTableDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-text-dialog</FILE>
+<TITLE>EEditorTextDialog</TITLE>
+EEditorTextDialog
+e_editor_text_dialog_new
+<SUBSECTION Standard>
+E_EDITOR_TEXT_DIALOG
+E_IS_EDITOR_TEXT_DIALOG
+E_TYPE_EDITOR_TEXT_DIALOG
+E_EDITOR_TEXT_DIALOG_CLASS
+E_IS_EDITOR_TEXT_DIALOG_CLASS
+E_EDITOR_TEXT_DIALOG_GET_CLASS
+EEditorTextDialogClass
+e_editor_text_dialog_get_type
+<SUBSECTION Private>
+EEditorTextDialogPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-editor-widget</FILE>
+<TITLE>EHTMLEditorView</TITLE>
+EHTMLEditorView
+e_html_editor_view_new
+e_html_editor_view_get_selection
+EHTMLEditorViewCommand
+e_html_editor_view_exec_command
+e_html_editor_view_get_changed
+e_html_editor_view_set_changed
+e_html_editor_view_get_html_mode
+e_html_editor_view_set_html_mode
+e_html_editor_view_get_inline_spelling
+e_html_editor_view_set_inline_spelling
+e_html_editor_view_get_magic_links
+e_html_editor_view_set_magic_links
+e_html_editor_view_get_magic_smileys
+e_html_editor_view_set_magic_smileys
+e_html_editor_view_get_spell_checker
+e_html_editor_view_get_text_html
+e_html_editor_view_get_text_plain
+e_html_editor_view_set_text_html
+e_html_editor_view_set_text_plain
+e_html_editor_view_paste_clipboard_quoted
+e_html_editor_view_update_fonts
+<SUBSECTION Standard>
+E_HTML_EDITOR_VIEW
+E_IS_HTML_EDITOR_VIEW
+E_TYPE_HTML_EDITOR_VIEW
+E_HTML_EDITOR_VIEW_CLASS
+E_IS_HTML_EDITOR_VIEW_CLASS
+E_HTML_EDITOR_VIEW_GET_CLASS
+EHTMLEditorViewClass
+e_html_editor_view_get_type
+<SUBSECTION Private>
+EHTMLEditorViewPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon</FILE>
+<TITLE>EEmoticon</TITLE>
+EEmoticon
+e_emoticon_equal
+e_emoticon_copy
+e_emoticon_free
+e_emoticon_get_uri
+<SUBSECTION Standard>
+E_TYPE_EMOTICON
+e_emoticon_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-action</FILE>
+<TITLE>EEmoticonAction</TITLE>
+EEmoticonAction
+e_emoticon_action_new
+<SUBSECTION Standard>
+E_EMOTICON_ACTION
+E_IS_EMOTICON_ACTION
+E_TYPE_EMOTICON_ACTION
+E_EMOTICON_ACTION_CLASS
+E_IS_EMOTICON_ACTION_CLASS
+E_EMOTICON_ACTION_GET_CLASS
+EEmoticonActionClass
+e_emoticon_action_get_type
+<SUBSECTION Private>
+EEmoticonActionPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-chooser</FILE>
+<TITLE>EEmoticonChooser</TITLE>
+EEmoticonChooser
+EEmoticonChooserInterface
+e_emoticon_chooser_get_current_emoticon
+e_emoticon_chooser_set_current_emoticon
+e_emoticon_chooser_item_activated
+e_emoticon_chooser_get_items
+e_emoticon_chooser_lookup_emoticon
+<SUBSECTION Standard>
+E_EMOTICON_CHOOSER
+E_IS_EMOTICON_CHOOSER
+E_TYPE_EMOTICON_CHOOSER
+E_EMOTICON_CHOOSER_GET_INTERFACE
+e_emoticon_chooser_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-chooser-menu</FILE>
+<TITLE>EEmoticonChooserMenu</TITLE>
+EEmoticonChooserMenu
+e_emoticon_chooser_menu_new
+<SUBSECTION Standard>
+E_EMOTICON_CHOOSER_MENU
+E_IS_EMOTICON_CHOOSER_MENU
+E_TYPE_EMOTICON_CHOOSER_MENU
+E_EMOTICON_CHOOSER_MENU_CLASS
+E_IS_EMOTICON_CHOOSER_MENU_CLASS
+E_EMOTICON_CHOOSER_MENU_GET_CLASS
+EEmoticonChooserMenuClass
+e_emoticon_chooser_menu_get_type
+<SUBSECTION Private>
+EEmoticonChooserMenuPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-emoticon-tool-button</FILE>
+<TITLE>EEmoticonToolButton</TITLE>
+EEmoticonToolButton
+e_emoticon_tool_button_new
+e_emoticon_tool_button_popup
+e_emoticon_tool_button_popdown
+<SUBSECTION Standard>
+E_EMOTICON_TOOL_BUTTON
+E_IS_EMOTICON_TOOL_BUTTON
+E_TYPE_EMOTICON_TOOL_BUTTON
+E_EMOTICON_TOOL_BUTTON_CLASS
+E_IS_EMOTICON_TOOL_BUTTON_CLASS
+E_EMOTICON_TOOL_BUTTON_GET_CLASS
+EEmoticonToolButtonClass
+e_emoticon_tool_button_get_type
+<SUBSECTION Private>
+EEmoticonToolButtonPrivate
+</SECTION>
+
+<SECTION>
 <FILE>e-event</FILE>
 <TITLE>EEvent</TITLE>
 EEvent
@@ -1908,6 +2841,25 @@ EImageChooserPrivate
 </SECTION>
 
 <SECTION>
+<FILE>e-image-chooser-dialog</FILE>
+<TITLE>EImageChooserDialog</TITLE>
+EImageChooserDialog
+e_image_chooser_dialog_new
+e_image_chooser_dialog_run
+<SUBSECTION Standard>
+E_IMAGE_CHOOSER_DIALOG
+E_IS_IMAGE_CHOOSER_DIALOG
+E_TYPE_IMAGE_CHOOSER_DIALOG
+E_IMAGE_CHOOSER_DIALOG_CLASS
+E_IS_IMAGE_CHOOSER_DIALOG_CLASS
+E_IMAGE_CHOOSER_DIALOG_GET_CLASS
+EImageChooserDialogClass
+e_image_chooser_dialog_get_type
+<SUBSECTION Private>
+EImageChooserDialogPrivate
+</SECTION>
+
+<SECTION>
 <FILE>e-import</FILE>
 <TITLE>EImport</TITLE>
 EImport
@@ -2055,6 +3007,7 @@ EMailSignatureComboBoxPrivate
 <TITLE>EMailSignatureEditor</TITLE>
 EMailSignatureEditor
 e_mail_signature_editor_new
+e_mail_signature_editor_get_editor
 e_mail_signature_editor_get_focus_tracker
 e_mail_signature_editor_get_registry
 e_mail_signature_editor_get_source
@@ -3392,13 +4345,71 @@ e_source_util_remote_delete
 </SECTION>
 
 <SECTION>
+<FILE>e-spell-checker</FILE>
+<TITLE>ESpellChecker</TITLE>
+ESpellChecker
+e_spell_checker_new
+e_spell_checker_list_available_dicts
+e_spell_checker_ref_dictionary
+e_spell_checker_get_enchant_dict
+e_spell_checker_get_language_active
+e_spell_checker_set_language_active
+e_spell_checker_list_active_languages
+e_spell_checker_count_active_languages
+e_spell_checker_check_word
+e_spell_checker_learn_word
+e_spell_checker_ignore_word
+<SUBSECTION Standard>
+E_SPELL_CHECKER
+E_IS_SPELL_CHECKER
+E_TYPE_SPELL_CHECKER
+E_SPELL_CHECKER_CLASS
+E_IS_SPELL_CHECKER_CLASS
+E_SPELL_CHECKER_GET_CLASS
+ESpellCheckerClass
+e_spell_checker_get_type
+<SUBSECTION Private>
+ESpellCheckerPrivate
+</SECTION>
+
+<SECTION>
+<FILE>e-spell-dictionary</FILE>
+<TITLE>ESpellDictionary</TITLE>
+ESpellDictionary
+e_spell_dictionary_new
+e_spell_dictionary_hash
+e_spell_dictionary_equal
+e_spell_dictionary_compare
+e_spell_dictionary_get_name
+e_spell_dictionary_get_code
+e_spell_dictionary_ref_spell_checker
+e_spell_dictionary_check_word
+e_spell_dictionary_learn_word
+e_spell_dictionary_ignore_word
+e_spell_dictionary_get_suggestions
+e_spell_dictionary_store_correction
+<SUBSECTION Standard>
+E_SPELL_DICTIONARY
+E_IS_SPELL_DICTIONARY
+E_TYPE_SPELL_DICTIONARY
+E_SPELL_DICTIONARY_CLASS
+E_IS_SPELL_DICTIONARY_CLASS
+E_SPELL_DICTIONARY_GET_CLASS
+ESpellDictionaryClass
+e_spell_dictionary_get_type
+<SUBSECTION Private>
+ESpellDictionaryPrivate
+</SECTION>
+
+<SECTION>
 <FILE>e-spell-entry</FILE>
 <TITLE>ESpellEntry</TITLE>
 ESpellEntry
 e_spell_entry_new
-e_spell_entry_set_languages
 e_spell_entry_get_checking_enabled
 e_spell_entry_set_checking_enabled
+e_spell_entry_get_spell_checker
+e_spell_entry_set_spell_checker
 <SUBSECTION Standard>
 E_SPELL_ENTRY
 E_IS_SPELL_ENTRY
@@ -4568,74 +5579,6 @@ EWebViewPrivate
 </SECTION>
 
 <SECTION>
-<FILE>e-web-view-gtkhtml</FILE>
-<TITLE>EWebViewGtkHTML</TITLE>
-EWebViewGtkHTML
-e_web_view_gtkhtml_new
-e_web_view_gtkhtml_clear
-e_web_view_gtkhtml_load_string
-e_web_view_gtkhtml_get_animate
-e_web_view_gtkhtml_set_animate
-e_web_view_gtkhtml_get_caret_mode
-e_web_view_gtkhtml_set_caret_mode
-e_web_view_gtkhtml_get_copy_target_list
-e_web_view_gtkhtml_get_disable_printing
-e_web_view_gtkhtml_set_disable_printing
-e_web_view_gtkhtml_get_disable_save_to_disk
-e_web_view_gtkhtml_set_disable_save_to_disk
-e_web_view_gtkhtml_get_editable
-e_web_view_gtkhtml_set_editable
-e_web_view_gtkhtml_get_inline_spelling
-e_web_view_gtkhtml_set_inline_spelling
-e_web_view_gtkhtml_get_magic_links
-e_web_view_gtkhtml_set_magic_links
-e_web_view_gtkhtml_get_magic_smileys
-e_web_view_gtkhtml_set_magic_smileys
-e_web_view_gtkhtml_get_selected_uri
-e_web_view_gtkhtml_set_selected_uri
-e_web_view_gtkhtml_get_cursor_image
-e_web_view_gtkhtml_set_cursor_image
-e_web_view_gtkhtml_get_open_proxy
-e_web_view_gtkhtml_set_open_proxy
-e_web_view_gtkhtml_get_paste_target_list
-e_web_view_gtkhtml_get_print_proxy
-e_web_view_gtkhtml_set_print_proxy
-e_web_view_gtkhtml_get_save_as_proxy
-e_web_view_gtkhtml_set_save_as_proxy
-e_web_view_gtkhtml_get_action
-e_web_view_gtkhtml_get_action_group
-e_web_view_gtkhtml_extract_uri
-e_web_view_gtkhtml_copy_clipboard
-e_web_view_gtkhtml_cut_clipboard
-e_web_view_gtkhtml_is_selection_active
-e_web_view_gtkhtml_paste_clipboard
-e_web_view_gtkhtml_scroll_forward
-e_web_view_gtkhtml_scroll_backward
-e_web_view_gtkhtml_select_all
-e_web_view_gtkhtml_unselect_all
-e_web_view_gtkhtml_zoom_100
-e_web_view_gtkhtml_zoom_in
-e_web_view_gtkhtml_zoom_out
-e_web_view_gtkhtml_get_ui_manager
-e_web_view_gtkhtml_get_popup_menu
-e_web_view_gtkhtml_show_popup_menu
-e_web_view_gtkhtml_status_message
-e_web_view_gtkhtml_stop_loading
-e_web_view_gtkhtml_update_actions
-<SUBSECTION Standard>
-E_WEB_VIEW_GTKHTML
-E_IS_WEB_VIEW_GTKHTML
-E_TYPE_WEB_VIEW_GTKHTML
-E_WEB_VIEW_GTKHTML_CLASS
-E_IS_WEB_VIEW_GTKHTML_CLASS
-E_WEB_VIEW_GTKHTML_GET_CLASS
-EWebViewGtkHTMLClass
-e_web_view_gtkhtml_get_type
-<SUBSECTION Private>
-EWebViewGtkHTMLPrivate
-</SECTION>
-
-<SECTION>
 <FILE>e-web-view-preview</FILE>
 <TITLE>EWebViewPreview</TITLE>
 EWebViewPreview
diff --git a/doc/reference/evolution-util/evolution-util.types 
b/doc/reference/evolution-util/evolution-util.types
index c47d820..8128567 100644
--- a/doc/reference/evolution-util/evolution-util.types
+++ b/doc/reference/evolution-util/evolution-util.types
@@ -55,12 +55,34 @@ e_charset_combo_box_get_type
 e_client_cache_get_type
 e_client_combo_box_get_type
 e_client_selector_get_type
+e_color_chooser_widget_get_type
+e_color_combo_get_type
 e_config_get_type
 e_config_hook_get_type
 e_contact_store_get_type
 e_data_capture_get_type
 e_date_edit_get_type
 e_destination_store_get_type
+e_html_editor_get_type
+e_html_editor_cell_dialog_get_type
+e_html_editor_dialog_get_type
+e_html_editor_find_dialog_get_type
+e_html_editor_hrule_dialog_get_type
+e_html_editor_image_dialog_get_type
+e_html_editor_link_dialog_get_type
+e_html_editor_page_dialog_get_type
+e_html_editor_paragraph_dialog_get_type
+e_html_editor_replace_dialog_get_type
+e_html_editor_selection_get_type
+e_html_editor_spell_check_dialog_get_type
+e_html_editor_table_dialog_get_type
+e_html_editor_text_dialog_get_type
+e_html_editor_view_get_type
+e_emoticon_action_get_type
+e_emoticon_chooser_get_type
+e_emoticon_chooser_menu_get_type
+e_emoticon_get_type
+e_emoticon_tool_button_get_type
 e_event_get_type
 e_event_hook_get_type
 e_file_request_get_type
@@ -75,6 +97,7 @@ e_filter_option_get_type
 e_filter_part_get_type
 e_filter_rule_get_type
 e_focus_tracker_get_type
+e_image_chooser_dialog_get_type
 e_image_chooser_get_type
 e_import_assistant_get_type
 e_import_get_type
@@ -123,6 +146,8 @@ e_source_config_dialog_get_type
 e_source_config_get_type
 e_source_selector_dialog_get_type
 e_source_selector_get_type
+e_spell_checker_get_type
+e_spell_dictionary_get_type
 e_spell_entry_get_type
 e_stock_request_get_type
 e_table_click_to_add_get_type
@@ -163,7 +188,6 @@ e_tree_table_adapter_get_type
 e_tree_view_frame_get_type
 e_url_entry_get_type
 e_web_view_get_type
-e_web_view_gtkhtml_get_type
 e_web_view_preview_get_type
 gal_view_collection_get_type
 gal_view_etable_get_type
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 66cdc22..6d9499d 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -40,6 +40,7 @@ errordir = $(privdatadir)/errors
 @EVO_PLUGIN_RULE@
 
 ui_DATA = \
+       e-html-editor-manager.ui \
        e-send-options.ui \
        e-table-config.ui \
        e-timezone-dialog.ui \
@@ -60,6 +61,7 @@ noinst_PROGRAMS = \
        test-category-completion \
        test-contact-store \
        test-dateedit \
+       test-html-editor \
        test-mail-signatures \
        test-name-selector \
        test-preferences-window \
@@ -99,7 +101,7 @@ libevolution_util_la_CPPFLAGS = \
        $(EVOLUTION_DATA_SERVER_CFLAGS) \
        $(GNOME_PLATFORM_CFLAGS) \
        $(GEO_CFLAGS) \
-       $(GTKHTML_CFLAGS) \
+       $(ENCHANT_CFLAGS) \
        $(GTKSPELL_CFLAGS) \
        $(CODE_COVERAGE_CFLAGS) \
        $(NULL)
@@ -165,6 +167,8 @@ evolution_util_include_HEADERS =  \
        e-client-cache.h \
        e-client-combo-box.h \
        e-client-selector.h \
+       e-color-chooser-widget.h \
+       e-color-combo.h \
        e-config.h \
        e-contact-store.h \
        e-data-capture.h \
@@ -173,6 +177,11 @@ evolution_util_include_HEADERS =  \
        e-destination-store.h \
        e-dialog-utils.h \
        e-dialog-widgets.h \
+       e-emoticon-action.h \
+       e-emoticon-chooser-menu.h \
+       e-emoticon-chooser.h \
+       e-emoticon-tool-button.h \
+       e-emoticon.h \
        e-event.h \
        e-file-request.h \
        e-file-utils.h \
@@ -187,9 +196,27 @@ evolution_util_include_HEADERS =  \
        e-filter-part.h \
        e-filter-rule.h \
        e-focus-tracker.h \
+       e-html-editor-actions.h \
+       e-html-editor-cell-dialog.h \
+       e-html-editor-dialog.h \
+       e-html-editor-find-dialog.h \
+       e-html-editor-hrule-dialog.h \
+       e-html-editor-image-dialog.h \
+       e-html-editor-link-dialog.h \
+       e-html-editor-page-dialog.h \
+       e-html-editor-paragraph-dialog.h \
+       e-html-editor-replace-dialog.h \
+       e-html-editor-selection.h \
+       e-html-editor-spell-check-dialog.h \
+       e-html-editor-table-dialog.h \
+       e-html-editor-text-dialog.h \
+       e-html-editor-utils.h \
+       e-html-editor-view.h \
+       e-html-editor.h \
        e-html-utils.h \
        e-icon-factory.h \
        e-image-chooser.h \
+       e-image-chooser-dialog.h \
        e-import-assistant.h \
        e-import.h \
        e-interval-chooser.h \
@@ -252,6 +279,8 @@ evolution_util_include_HEADERS =  \
        e-source-selector-dialog.h \
        e-source-selector.h \
        e-source-util.h \
+       e-spell-checker.h \
+       e-spell-dictionary.h \
        e-spell-entry.h \
        e-spell-text-view.h \
        e-stock-request.h \
@@ -305,7 +334,6 @@ evolution_util_include_HEADERS =  \
        e-url-entry.h \
        e-util-enums.h \
        e-util-enumtypes.h \
-       e-web-view-gtkhtml.h \
        e-web-view-preview.h \
        e-web-view.h \
        e-widget-undo.h \
@@ -410,6 +438,8 @@ libevolution_util_la_SOURCES = \
        e-client-cache.c \
        e-client-combo-box.c \
        e-client-selector.c \
+       e-color-chooser-widget.c \
+       e-color-combo.c \
        e-config.c \
        e-contact-store.c \
        e-data-capture.c \
@@ -418,6 +448,11 @@ libevolution_util_la_SOURCES = \
        e-destination-store.c \
        e-dialog-utils.c \
        e-dialog-widgets.c \
+       e-emoticon-action.c \
+       e-emoticon-chooser-menu.c \
+       e-emoticon-chooser.c \
+       e-emoticon-tool-button.c \
+       e-emoticon.c \
        e-event.c \
        e-file-request.c \
        e-file-utils.c \
@@ -432,9 +467,28 @@ libevolution_util_la_SOURCES = \
        e-filter-part.c \
        e-filter-rule.c \
        e-focus-tracker.c \
+       e-html-editor-actions.c \
+       e-html-editor-cell-dialog.c \
+       e-html-editor-dialog.c \
+       e-html-editor-find-dialog.c \
+       e-html-editor-hrule-dialog.c \
+       e-html-editor-image-dialog.c \
+       e-html-editor-link-dialog.c \
+       e-html-editor-page-dialog.c \
+       e-html-editor-paragraph-dialog.c \
+       e-html-editor-private.h \
+       e-html-editor-replace-dialog.c \
+       e-html-editor-selection.c \
+       e-html-editor-spell-check-dialog.c \
+       e-html-editor-table-dialog.c \
+       e-html-editor-text-dialog.c \
+       e-html-editor-utils.c \
+       e-html-editor-view.c \
+       e-html-editor.c \
        e-html-utils.c \
        e-icon-factory.c \
        e-image-chooser.c \
+       e-image-chooser-dialog.c \
        e-import-assistant.c \
        e-import.c \
        e-interval-chooser.c \
@@ -497,6 +551,8 @@ libevolution_util_la_SOURCES = \
        e-source-selector-dialog.c \
        e-source-selector.c \
        e-source-util.c \
+       e-spell-checker.c \
+       e-spell-dictionary.c \
        e-spell-entry.c \
        e-spell-text-view.c \
        e-stock-request.c \
@@ -547,7 +603,6 @@ libevolution_util_la_SOURCES = \
        e-url-entry.c \
        e-util-enumtypes.c \
        e-util-private.h \
-       e-web-view-gtkhtml.c \
        e-web-view-preview.c \
        e-web-view.c \
        e-widget-undo.c \
@@ -590,7 +645,7 @@ libevolution_util_la_LIBADD =  \
        $(EVOLUTION_DATA_SERVER_LIBS) \
        $(GNOME_PLATFORM_LIBS) \
        $(GEO_LIBS) \
-       $(GTKHTML_LIBS) \
+       $(ENCHANT_LIBS) \
        $(GTKSPELL_LIBS) \
        $(INTLLIBS) \
        $(MATH_LIB) \
@@ -625,6 +680,10 @@ test_dateedit_CPPFLAGS = $(TEST_CPPFLAGS)
 test_dateedit_SOURCES = test-dateedit.c
 test_dateedit_LDADD = $(TEST_LDADD)
 
+test_html_editor_CPPFLAGS = $(TEST_CPPFLAGS)
+test_html_editor_SOURCES = test-html-editor.c
+test_html_editor_LDADD = $(TEST_LDADD)
+
 test_mail_signatures_CPPFLAGS = $(TEST_CPPFLAGS)
 test_mail_signatures_SOURCES = test-mail-signatures.c
 test_mail_signatures_LDADD = $(TEST_LDADD)
diff --git a/e-util/e-action-combo-box.c b/e-util/e-action-combo-box.c
index 1b784b8..33d678a 100644
--- a/e-util/e-action-combo-box.c
+++ b/e-util/e-action-combo-box.c
@@ -104,10 +104,6 @@ action_combo_box_render_pixbuf (GtkCellLayout *layout,
        gboolean visible;
        gint width;
 
-       /* Do any of the actions have an icon? */
-       if (!combo_box->priv->group_has_icons)
-               return;
-
        gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
 
        /* A NULL action means the row is a separator. */
@@ -122,8 +118,12 @@ action_combo_box_render_pixbuf (GtkCellLayout *layout,
                "visible", &visible,
                NULL);
 
-       /* Keep the pixbuf renderer a fixed size for proper alignment. */
-       gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
+       /* If some action has an icon */
+       if (combo_box->priv->group_has_icons)
+               /* Keep the pixbuf renderer a fixed size for proper alignment. */
+               gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
+       else
+               width = 0;
 
        /* We can't set both "icon-name" and "stock-id" because setting
         * one unsets the other.  So pick the one that has a non-NULL
@@ -245,13 +245,25 @@ action_combo_box_update_model (EActionComboBox *combo_box)
                GtkRadioAction *action = list->data;
                GtkTreePath *path;
                GtkTreeIter iter;
-               gchar *icon_name;
-               gchar *stock_id;
+               gchar *icon_name = NULL;
+               gchar *stock_id = NULL;
+               gboolean visible = FALSE;
                gint value;
 
-               g_object_get (
-                       action, "icon-name", &icon_name,
-                       "stock-id", &stock_id, NULL);
+               g_object_get (action,
+                       "icon-name", &icon_name,
+                       "stock-id", &stock_id,
+                       "visible", &visible,
+                       NULL);
+
+               if (!visible) {
+                       g_free (icon_name);
+                       g_free (stock_id);
+
+                       list = g_slist_next (list);
+                       continue;
+               }
+
                combo_box->priv->group_has_icons |=
                        (icon_name != NULL || stock_id != NULL);
                g_free (icon_name);
@@ -583,3 +595,11 @@ e_action_combo_box_add_separator_after (EActionComboBox *combo_box,
                GTK_LIST_STORE (model), &iter, COLUMN_ACTION,
                NULL, COLUMN_SORT, (gfloat) action_value + 0.5, -1);
 }
+
+void
+e_action_combo_box_update_model (EActionComboBox *combo_box)
+{
+       g_return_if_fail (E_IS_ACTION_COMBO_BOX (combo_box));
+
+       action_combo_box_update_model (combo_box);
+}
diff --git a/e-util/e-action-combo-box.h b/e-util/e-action-combo-box.h
index f3a5a0b..3a44ed7 100644
--- a/e-util/e-action-combo-box.h
+++ b/e-util/e-action-combo-box.h
@@ -81,6 +81,7 @@ void          e_action_combo_box_add_separator_before
 void           e_action_combo_box_add_separator_after
                                                (EActionComboBox *combo_box,
                                                 gint action_value);
+void           e_action_combo_box_update_model (EActionComboBox *combo_box);
 
 G_END_DECLS
 
diff --git a/e-util/e-color-chooser-widget.c b/e-util/e-color-chooser-widget.c
new file mode 100644
index 0000000..5761ebf
--- /dev/null
+++ b/e-util/e-color-chooser-widget.c
@@ -0,0 +1,253 @@
+
+/* e-color-chooser-widget.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-color-chooser-widget.h"
+
+#include <glib/gi18n-lib.h>
+
+#define E_COLOR_CHOOSER_WIDGET_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_COLOR_CHOOSER_WIDGET, EColorChooserWidgetPrivate))
+
+/**
+ * EColorChooserWidget:
+ *
+ * This widget wrapps around #GtkColorChooserWidget and allows the widget to be
+ * used as a delegate for #GtkComboBox for instance.
+ */
+
+struct _EColorChooserWidgetPrivate {
+       gboolean showing_editor;
+};
+
+enum {
+       SIGNAL_EDITOR_ACTIVATED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (
+       EColorChooserWidget,
+       e_color_chooser_widget,
+       GTK_TYPE_COLOR_CHOOSER_WIDGET);
+
+/* UGLY UGLY UGLY!
+ * GtkColorChooserWidget sends "color-activated" signal
+ * only when user double-clicks the color. This is totally stupid
+ * and since we want to use it in a combobox-like widget, we need
+ * to be notified upon single click (which by default only selects the color).
+ *
+ * Unfortunatelly the GtkColorSwatch widget, which handles the button-press
+ * event is a non-public widget embedded within the GtkColorChooserWidget,
+ * so we can't just subclass it and fix the behavior.
+ *
+ * Here we override button_press_event of the GtkColorSwatch and manually
+ * emit the 'activate' signal on single click. This is stupid, ugly and I
+ * want to punch someone for such a stupid design...
+ */
+static gboolean
+color_chooser_widget_button_press_event (GtkWidget *widget,
+                                         GdkEventButton *event)
+{
+       if ((event->type == GDK_BUTTON_PRESS) &&
+           (event->button == GDK_BUTTON_PRIMARY)) {
+
+               g_signal_emit_by_name (widget, "activate");
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+color_chooser_widget_color_activated (GtkColorChooser *chooser,
+                                      GdkRGBA *color,
+                                      gpointer user_data)
+{
+       /* Because we are simulating the double-click by accepting only
+        * single click, the color in the swatch is actually not selected,
+        * so we must do it manually */
+       gtk_color_chooser_set_rgba (chooser, color);
+}
+
+static gboolean
+run_color_chooser_dialog (gpointer user_data)
+{
+       EColorChooserWidgetPrivate *priv;
+       GtkWidget *parent_window;
+       GtkWidget *parent_chooser;
+       GtkWidget *dialog;
+       GtkWidget *chooser;
+
+       parent_chooser = user_data;
+
+       g_object_set (
+               G_OBJECT (parent_chooser), "show-editor", FALSE, NULL);
+
+       parent_window = g_object_get_data (G_OBJECT (parent_chooser), "window");
+       if (!parent_window)
+               parent_window = gtk_widget_get_toplevel (parent_chooser);
+       dialog = gtk_dialog_new_with_buttons (
+               N_("Choose custom color"),
+               GTK_WINDOW (parent_window),
+               GTK_DIALOG_MODAL,
+               GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+               GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
+
+       chooser = gtk_color_chooser_widget_new ();
+       g_object_set (G_OBJECT (chooser), "show-editor", TRUE, NULL);
+       gtk_box_pack_start (
+               GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+               chooser, TRUE, TRUE, 5);
+
+       gtk_widget_show_all (chooser);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+               GdkRGBA color;
+
+               gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (chooser), &color);
+               gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (parent_chooser), &color);
+
+               g_signal_emit_by_name (parent_chooser, "color-activated", &color);
+       }
+
+       gtk_widget_destroy (dialog);
+
+       priv = E_COLOR_CHOOSER_WIDGET_GET_PRIVATE (parent_chooser);
+       priv->showing_editor = FALSE;
+
+       return FALSE;
+}
+
+static void
+color_chooser_show_editor_notify_cb (EColorChooserWidget *chooser,
+                                     GParamSpec *pspec,
+                                     gpointer user_data)
+{
+       gboolean show_editor;
+
+       g_object_get (G_OBJECT (chooser), "show-editor", &show_editor, NULL);
+
+       /* Nothing to do here... */
+       if ((show_editor == FALSE) || (chooser->priv->showing_editor == TRUE))
+               return;
+
+       chooser->priv->showing_editor = TRUE;
+
+       /* Hide the editor - we don't want to display the single-color editor
+        * within this widget. We rather create a dialog window with the editor
+        * (we can't do it from this callback as Gtk would stop it in order to
+        * prevent endless recursion probably) */
+       g_idle_add (run_color_chooser_dialog, chooser);
+       g_signal_emit (chooser, signals[SIGNAL_EDITOR_ACTIVATED], 0);
+}
+
+void
+e_color_chooser_widget_class_init (EColorChooserWidgetClass *class)
+{
+       g_type_class_add_private (class, sizeof (EColorChooserWidgetPrivate));
+
+       signals[SIGNAL_EDITOR_ACTIVATED] = g_signal_new (
+               "editor-activated",
+               E_TYPE_COLOR_CHOOSER_WIDGET,
+               G_SIGNAL_RUN_FIRST,
+               G_STRUCT_OFFSET (EColorChooserWidgetClass, editor_activated),
+               NULL,
+               NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+}
+
+/* Recursively go through GtkContainers within the GtkColorChooserWidget
+ * and try to find GtkColorSwatch widget. */
+static GtkWidget *
+find_swatch (GtkContainer *container)
+{
+       GList *children, *child;
+
+       children = gtk_container_get_children (container);
+       for (child = children; child; child = g_list_next (child)) {
+               GtkWidget *widget = child->data;
+               GtkWidget *swatch;
+
+               if (GTK_IS_CONTAINER (widget)) {
+                       swatch = find_swatch (GTK_CONTAINER (widget));
+
+                       if (swatch != NULL) {
+                               g_list_free (children);
+                               return swatch;
+                       }
+               }
+
+               if (g_strcmp0 (G_OBJECT_TYPE_NAME (widget), "GtkColorSwatch") == 0) {
+                       g_list_free (children);
+                       return widget;
+               }
+       }
+
+       g_list_free (children);
+
+       return NULL;
+}
+
+void
+e_color_chooser_widget_init (EColorChooserWidget *widget)
+{
+       GtkWidget *swatch;
+
+       widget->priv = E_COLOR_CHOOSER_WIDGET_GET_PRIVATE (widget);
+       widget->priv->showing_editor = FALSE;
+
+       swatch = find_swatch (GTK_CONTAINER (widget));
+
+       /* If swatch is NULL then GTK changed something and this widget
+        * becomes broken... */
+       g_return_if_fail (swatch != NULL);
+
+       if (swatch) {
+               GtkWidgetClass *swatch_class;
+               swatch_class = GTK_WIDGET_GET_CLASS (swatch);
+               swatch_class->button_press_event = color_chooser_widget_button_press_event;
+       }
+
+       g_signal_connect (
+               widget, "color-activated",
+               G_CALLBACK (color_chooser_widget_color_activated), NULL);
+
+       g_signal_connect (
+               widget, "notify::show-editor",
+               G_CALLBACK (color_chooser_show_editor_notify_cb), NULL);
+}
+
+GtkWidget *
+e_color_chooser_widget_new (void)
+{
+       return g_object_new (
+               E_TYPE_COLOR_CHOOSER_WIDGET,
+               "show-editor", FALSE,
+               "use-alpha", FALSE,
+               NULL);
+}
diff --git a/e-util/e-color-chooser-widget.h b/e-util/e-color-chooser-widget.h
new file mode 100644
index 0000000..3b7c60d
--- /dev/null
+++ b/e-util/e-color-chooser-widget.h
@@ -0,0 +1,71 @@
+/* e-color-chooser-widget.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_COLOR_CHOOSER_WIDGET_H
+#define E_COLOR_CHOOSER_WIDGET_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_COLOR_CHOOSER_WIDGET \
+       (e_color_chooser_widget_get_type ())
+#define E_COLOR_CHOOSER_WIDGET(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COLOR_CHOOSER_WIDGET, EColorChooserWidget))
+#define E_COLOR_CHOOSER_WIDGET_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COLOR_CHOOSER_WIDGET, EColorChooserWidgetClass))
+#define E_IS_COLOR_CHOOSER_WIDGET(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COLOR_CHOOSER_WIDGET))
+#define E_IS_COLOR_CHOOSER_WIDGET_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COLOR_CHOOSER_WIDGET))
+#define E_COLOR_CHOOSER_WIDGET_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COLOR_CHOOSER_WIDGET, EColorChooserWidgetClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EColorChooserWidget EColorChooserWidget;
+typedef struct _EColorChooserWidgetClass EColorChooserWidgetClass;
+typedef struct _EColorChooserWidgetPrivate EColorChooserWidgetPrivate;
+
+struct _EColorChooserWidget {
+       GtkColorChooserWidget parent;
+       EColorChooserWidgetPrivate *priv;
+};
+
+struct _EColorChooserWidgetClass {
+       GtkColorChooserWidgetClass parent_class;
+
+       void            (*editor_activated)     (GtkColorChooserWidget *chooser);
+};
+
+GType          e_color_chooser_widget_get_type (void) G_GNUC_CONST;
+GtkWidget *    e_color_chooser_widget_new      (void);
+
+G_END_DECLS
+
+#endif /* E_COLOR_CHOOSER_WIDGET_H */
+
diff --git a/e-util/e-color-combo.c b/e-util/e-color-combo.c
new file mode 100644
index 0000000..f2f46e4
--- /dev/null
+++ b/e-util/e-color-combo.c
@@ -0,0 +1,976 @@
+/* e-color-combo.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-color-combo.h"
+#include "e-color-chooser-widget.h"
+
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+#include <cairo/cairo.h>
+#include <alloca.h>
+
+#define E_COLOR_COMBO_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_COLOR_COMBO, EColorComboPrivate))
+
+struct _EColorComboPrivate {
+       GtkWidget *color_frame;         /* not referenced */
+       GtkWidget *arrow;               /* not referenced */
+
+       GtkWidget *window;
+       GtkWidget *default_button;      /* not referenced */
+       GtkWidget *chooser_widget;      /* not referenced */
+
+       guint popup_shown       : 1;
+       guint popup_in_progress : 1;
+
+       GdkRGBA *current_color;
+       GdkRGBA *default_color;
+       gint default_transparent: 1;
+
+       GList *palette;
+
+       GdkDevice *grab_keyboard;
+       GdkDevice *grab_mouse;
+};
+
+enum {
+       PROP_0,
+       PROP_CURRENT_COLOR,
+       PROP_DEFAULT_COLOR,
+       PROP_DEFAULT_LABEL,
+       PROP_DEFAULT_TRANSPARENT,
+       PROP_PALETTE,
+       PROP_POPUP_SHOWN
+};
+
+enum {
+       ACTIVATED,
+       POPUP,
+       POPDOWN,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+static GdkRGBA black = { 0, 0, 0, 1 };
+
+static struct {
+       const gchar *color;
+       const gchar *tooltip;
+} default_colors[] = {
+
+       { "#000000", N_("black") },
+       { "#993300", N_("light brown") },
+       { "#333300", N_("brown gold") },
+       { "#003300", N_("dark green #2") },
+       { "#003366", N_("navy") },
+       { "#000080", N_("dark blue") },
+       { "#333399", N_("purple #2") },
+       { "#333333", N_("very dark gray") },
+
+       { "#800000", N_("dark red") },
+       { "#FF6600", N_("red-orange") },
+       { "#808000", N_("gold") },
+       { "#008000", N_("dark green") },
+       { "#008080", N_("dull blue") },
+       { "#0000FF", N_("blue") },
+       { "#666699", N_("dull purple") },
+       { "#808080", N_("dark grey") },
+
+       { "#FF0000", N_("red") },
+       { "#FF9900", N_("orange") },
+       { "#99CC00", N_("lime") },
+       { "#339966", N_("dull green") },
+       { "#33CCCC", N_("dull blue #2") },
+       { "#3366FF", N_("sky blue #2") },
+       { "#800080", N_("purple") },
+       { "#969696", N_("gray") },
+
+       { "#FF00FF", N_("magenta") },
+       { "#FFCC00", N_("bright orange") },
+       { "#FFFF00", N_("yellow") },
+       { "#00FF00", N_("green") },
+       { "#00FFFF", N_("cyan") },
+       { "#00CCFF", N_("bright blue") },
+       { "#993366", N_("red purple") },
+       { "#C0C0C0", N_("light grey") },
+
+       { "#FF99CC", N_("pink") },
+       { "#FFCC99", N_("light orange") },
+       { "#FFFF99", N_("light yellow") },
+       { "#CCFFCC", N_("light green") },
+       { "#CCFFFF", N_("light cyan") },
+       { "#99CCFF", N_("light blue") },
+       { "#CC99FF", N_("light purple") },
+       { "#FFFFFF", N_("white") }
+};
+
+G_DEFINE_TYPE (
+       EColorCombo,
+       e_color_combo,
+       GTK_TYPE_BUTTON);
+
+static void
+color_combo_reposition_window (EColorCombo *combo)
+{
+       GdkScreen *screen;
+       GdkWindow *window;
+       GdkRectangle monitor;
+       GtkAllocation allocation;
+       gint monitor_num;
+       gint x, y, width, height;
+
+       screen = gtk_widget_get_screen (GTK_WIDGET (combo));
+       window = gtk_widget_get_window (GTK_WIDGET (combo));
+       monitor_num = gdk_screen_get_monitor_at_window (screen, window);
+       gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+       gdk_window_get_origin (window, &x, &y);
+
+       if (!gtk_widget_get_has_window (GTK_WIDGET (combo))) {
+               gtk_widget_get_allocation (GTK_WIDGET (combo), &allocation);
+               x += allocation.x;
+               y += allocation.y;
+       }
+
+       gtk_widget_get_allocation (combo->priv->window, &allocation);
+       width = allocation.width;
+       height = allocation.height;
+
+       x = CLAMP (x, monitor.x, monitor.x + monitor.width - width);
+       y = CLAMP (y, monitor.y, monitor.y + monitor.height - height);
+
+       gtk_window_move (GTK_WINDOW (combo->priv->window), x, y);
+}
+
+static void
+color_combo_popup (EColorCombo *combo)
+{
+       GdkWindow *window;
+       gboolean grab_status;
+       GdkDevice *device, *mouse, *keyboard;
+       guint32 activate_time;
+
+       device = gtk_get_current_event_device ();
+       g_return_if_fail (device != NULL);
+
+       if (!gtk_widget_get_realized (GTK_WIDGET (combo)))
+               return;
+
+       if (combo->priv->popup_shown)
+               return;
+
+       activate_time = gtk_get_current_event_time ();
+       if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) {
+               keyboard = device;
+               mouse = gdk_device_get_associated_device (device);
+       } else {
+               keyboard = gdk_device_get_associated_device (device);
+               mouse = device;
+       }
+
+       /* Position the window over the button. */
+       color_combo_reposition_window (combo);
+
+       /* Show the pop-up. */
+       gtk_widget_show_all (combo->priv->window);
+       gtk_widget_grab_focus (combo->priv->window);
+
+       /* Try to grab the pointer and keyboard. */
+       window = gtk_widget_get_window (combo->priv->window);
+       grab_status =
+               (keyboard == NULL) ||
+               (gdk_device_grab (
+                       keyboard, window,
+                       GDK_OWNERSHIP_WINDOW, TRUE,
+                       GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+                       NULL, activate_time) == GDK_GRAB_SUCCESS);
+       if (grab_status) {
+               grab_status =
+                       (mouse == NULL) ||
+                       (gdk_device_grab (
+                               mouse, window,
+                               GDK_OWNERSHIP_WINDOW, TRUE,
+                               GDK_BUTTON_PRESS_MASK |
+                               GDK_BUTTON_RELEASE_MASK |
+                               GDK_POINTER_MOTION_MASK,
+                               NULL, activate_time) == GDK_GRAB_SUCCESS);
+               if (!grab_status && keyboard)
+                       gdk_device_ungrab (keyboard, activate_time);
+       }
+
+       if (grab_status) {
+               gtk_device_grab_add (combo->priv->window, mouse, TRUE);
+               combo->priv->grab_keyboard = keyboard;
+               combo->priv->grab_mouse = mouse;
+       } else {
+               gtk_widget_hide (combo->priv->window);
+       }
+
+       /* Always make sure the editor-mode is OFF */
+       g_object_set (
+               G_OBJECT (combo->priv->chooser_widget),
+               "show-editor", FALSE, NULL);
+}
+
+static void
+color_combo_popdown (EColorCombo *combo)
+{
+       if (!gtk_widget_get_realized (GTK_WIDGET (combo)))
+               return;
+
+       if (!combo->priv->popup_shown)
+               return;
+
+       /* Hide the pop-up. */
+       gtk_device_grab_remove (combo->priv->window, combo->priv->grab_mouse);
+       gtk_widget_hide (combo->priv->window);
+
+       if (combo->priv->grab_keyboard)
+               gdk_device_ungrab (combo->priv->grab_keyboard, GDK_CURRENT_TIME);
+       if (combo->priv->grab_mouse)
+               gdk_device_ungrab (combo->priv->grab_mouse, GDK_CURRENT_TIME);
+
+       combo->priv->grab_keyboard = NULL;
+       combo->priv->grab_mouse = NULL;
+}
+
+static gboolean
+color_combo_window_button_press_event_cb (EColorCombo *combo,
+                                          GdkEvent *event,
+                                          gpointer user_data)
+{
+       GtkWidget *event_widget;
+
+       event_widget = gtk_get_event_widget ((GdkEvent *) event);
+
+       if (event_widget == combo->priv->window)
+               return TRUE;
+
+       if (combo->priv->popup_shown == TRUE)
+               return FALSE;
+
+       color_combo_popup (combo);
+
+       combo->priv->popup_in_progress = TRUE;
+
+       return TRUE;
+}
+
+static gboolean
+color_combo_window_button_release_event_cb (EColorCombo *combo,
+                                            GdkEvent *event,
+                                            gpointer user_data)
+{
+       gboolean popup_in_progress;
+
+       popup_in_progress = combo->priv->popup_in_progress;
+       combo->priv->popup_in_progress = FALSE;
+
+       if (popup_in_progress)
+               return FALSE;
+
+       if (combo->priv->popup_shown)
+               goto popdown;
+
+       return FALSE;
+
+popdown:
+       color_combo_popdown (combo);
+
+       return TRUE;
+}
+
+static void
+color_combo_child_show_cb (EColorCombo *combo)
+{
+       combo->priv->popup_shown = TRUE;
+       g_object_notify (G_OBJECT (combo), "popup-shown");
+}
+
+static void
+color_combo_child_hide_cb (EColorCombo *combo)
+{
+       combo->priv->popup_shown = FALSE;
+       g_object_notify (G_OBJECT (combo), "popup-shown");
+}
+
+static void
+color_combo_get_preferred_width (GtkWidget *widget,
+                                 gint *min_width,
+                                 gint *natural_width)
+{
+       GtkWidgetClass *widget_class;
+
+       widget_class = GTK_WIDGET_CLASS (e_color_combo_parent_class);
+       widget_class->get_preferred_width (widget, min_width, natural_width);
+
+       /* Make sure the box with color sample is always visible */
+       if (min_width)
+               *min_width += 20;
+
+       if (natural_width)
+               *natural_width += 20;
+}
+
+static gboolean
+color_combo_button_press_event_cb (GtkWidget *widget,
+                                   GdkEventButton *event)
+{
+       EColorCombo *combo = E_COLOR_COMBO (widget);
+       GdkWindow *window;
+       gint x, y, width, height;
+
+       window = gtk_widget_get_window (combo->priv->color_frame);
+       gdk_window_get_position (window, &x, &y);
+       /* Width - only width of the frame with color box */
+       width = gtk_widget_get_allocated_width (combo->priv->color_frame);
+
+       /* Height - height of the entire button (widget) */
+       height = gtk_widget_get_allocated_height (widget);
+
+       /* Check whether user clicked on the color frame - in such case
+        * apply the color immediatelly without displaying the popup widget */
+       if ((event->x_root >= x) && (event->x_root <= x + width) &&
+           (event->y_root >= y) && (event->y_root <= y + height)) {
+               GdkRGBA color;
+
+               e_color_combo_get_current_color (combo, &color);
+               g_signal_emit (combo, signals[ACTIVATED], 0, &color);
+
+               return TRUE;
+       }
+
+       /* Otherwise display the popup widget */
+       if (combo->priv->popup_shown) {
+               color_combo_popdown (combo);
+       } else {
+               color_combo_popup (combo);
+       }
+
+       return FALSE;
+}
+
+static void
+color_combo_swatch_color_changed (EColorCombo *combo,
+                                  GdkRGBA *color,
+                                  gpointer user_data)
+{
+       g_signal_emit (combo, signals[ACTIVATED], 0, color);
+
+       e_color_combo_set_current_color (combo, color);
+
+       color_combo_popdown (combo);
+}
+
+static void
+color_combo_draw_frame_cb (GtkWidget *widget,
+                           cairo_t *cr,
+                           gpointer user_data)
+{
+       EColorCombo *combo = user_data;
+       GdkRGBA rgba;
+       GtkAllocation allocation;
+       gint height, width;
+
+       e_color_combo_get_current_color (combo, &rgba);
+
+       gtk_widget_get_allocation (widget, &allocation);
+       width = allocation.width;
+       height = allocation.height;
+
+       cairo_rectangle (cr, 0, 0, width - 10, height);
+       cairo_set_source_rgb (cr, rgba.red, rgba.green, rgba.blue);
+       cairo_fill (cr);
+}
+
+static void
+color_combo_set_default_color_cb (EColorCombo *combo,
+                                  gpointer user_data)
+{
+       GdkRGBA color;
+
+       e_color_combo_get_default_color (combo, &color);
+       e_color_combo_set_current_color (combo, &color);
+
+       g_signal_emit (combo, signals[ACTIVATED], 0, &color);
+}
+
+static void
+color_combo_set_property (GObject *object,
+                          guint property_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CURRENT_COLOR:
+                       e_color_combo_set_current_color (
+                               E_COLOR_COMBO (object),
+                               g_value_get_boxed (value));
+                       return;
+
+               case PROP_DEFAULT_COLOR:
+                       e_color_combo_set_default_color (
+                               E_COLOR_COMBO (object),
+                               g_value_get_boxed (value));
+                       return;
+
+               case PROP_DEFAULT_LABEL:
+                       e_color_combo_set_default_label (
+                               E_COLOR_COMBO (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_DEFAULT_TRANSPARENT:
+                       e_color_combo_set_default_transparent (
+                               E_COLOR_COMBO (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_PALETTE:
+                       e_color_combo_set_palette (
+                               E_COLOR_COMBO (object),
+                               g_value_get_object (value));
+                       return;
+
+               case PROP_POPUP_SHOWN:
+                       if (g_value_get_boolean (value))
+                               e_color_combo_popup (
+                                       E_COLOR_COMBO (object));
+                       else
+                               e_color_combo_popdown (
+                                       E_COLOR_COMBO (object));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+color_combo_get_property (GObject *object,
+                          guint property_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+       EColorComboPrivate *priv;
+       GdkRGBA color;
+
+       priv = E_COLOR_COMBO_GET_PRIVATE (object);
+
+       switch (property_id) {
+               case PROP_CURRENT_COLOR:
+                       e_color_combo_get_current_color (
+                               E_COLOR_COMBO (object), &color);
+                       g_value_set_boxed (value, &color);
+                       return;
+
+               case PROP_DEFAULT_COLOR:
+                       e_color_combo_get_default_color (
+                               E_COLOR_COMBO (object), &color);
+                       g_value_set_boxed (value, &color);
+                       return;
+
+               case PROP_DEFAULT_LABEL:
+                       g_value_set_string (
+                               value, e_color_combo_get_default_label (
+                               E_COLOR_COMBO (object)));
+                       return;
+
+               case PROP_DEFAULT_TRANSPARENT:
+                       g_value_set_boolean (
+                               value,
+                               e_color_combo_get_default_transparent (
+                               E_COLOR_COMBO (object)));
+                       return;
+
+               case PROP_PALETTE:
+                       g_value_set_object (
+                               value, e_color_combo_get_palette (
+                               E_COLOR_COMBO (object)));
+                       return;
+
+               case PROP_POPUP_SHOWN:
+                       g_value_set_boolean (value, priv->popup_shown);
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+color_combo_dispose (GObject *object)
+{
+       EColorComboPrivate *priv;
+
+       priv = E_COLOR_COMBO_GET_PRIVATE (object);
+
+       if (priv->window != NULL) {
+               gtk_widget_destroy (priv->window);
+               priv->window = NULL;
+       }
+
+       if (priv->current_color != NULL) {
+               gdk_rgba_free (priv->current_color);
+               priv->current_color = NULL;
+       }
+
+       if (priv->default_color != NULL) {
+               gdk_rgba_free (priv->default_color);
+               priv->default_color = NULL;
+       }
+
+       g_list_free_full (priv->palette, (GDestroyNotify) gdk_rgba_free);
+       priv->palette = NULL;
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_color_combo_parent_class)->dispose (object);
+}
+
+static void
+e_color_combo_class_init (EColorComboClass *class)
+{
+       GObjectClass *object_class;
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EColorComboPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = color_combo_set_property;
+       object_class->get_property = color_combo_get_property;
+       object_class->dispose = color_combo_dispose;
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->get_preferred_width = color_combo_get_preferred_width;
+       widget_class->button_press_event = color_combo_button_press_event_cb;
+
+       class->popup = color_combo_popup;
+       class->popdown = color_combo_popdown;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_CURRENT_COLOR,
+               g_param_spec_boxed (
+                       "current-color",
+                       "Current color",
+                       "The currently selected color",
+                       GDK_TYPE_RGBA,
+                       G_PARAM_READWRITE));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_DEFAULT_COLOR,
+               g_param_spec_boxed (
+                       "default-color",
+                       "Default color",
+                       "The color associated with the default button",
+                       GDK_TYPE_RGBA,
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_READWRITE));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_DEFAULT_LABEL,
+               g_param_spec_string (
+                       "default-label",
+                       "Default label",
+                       "The label for the default button",
+                       _("Default"),
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_READWRITE));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_DEFAULT_TRANSPARENT,
+               g_param_spec_boolean (
+                       "default-transparent",
+                       "Default is transparent",
+                       "Whether the default color is transparent",
+                       FALSE,
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_READWRITE));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_PALETTE,
+               g_param_spec_pointer (
+                       "palette",
+                       "Color palette",
+                       "Custom color palette",
+                       G_PARAM_READWRITE));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_POPUP_SHOWN,
+               g_param_spec_boolean (
+                       "popup-shown",
+                       "Popup shown",
+                       "Whether the combo's dropdown is shown",
+                       FALSE,
+                       G_PARAM_READWRITE));
+
+       signals[ACTIVATED] = g_signal_new (
+               "activated",
+               G_OBJECT_CLASS_TYPE (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EColorComboClass, activated),
+               NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+
+       signals[POPUP] = g_signal_new (
+               "popup",
+               G_OBJECT_CLASS_TYPE (class),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EColorComboClass, popup),
+               NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+
+       signals[POPDOWN] = g_signal_new (
+               "popdown",
+               G_OBJECT_CLASS_TYPE (class),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EColorComboClass, popdown),
+               NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+
+       gtk_binding_entry_add_signal (
+               gtk_binding_set_by_class (class),
+               GDK_KEY_Down, GDK_MOD1_MASK, "popup", 0);
+       gtk_binding_entry_add_signal (
+               gtk_binding_set_by_class (class),
+               GDK_KEY_KP_Down, GDK_MOD1_MASK, "popup", 0);
+
+       gtk_binding_entry_add_signal (
+               gtk_binding_set_by_class (class),
+               GDK_KEY_Up, GDK_MOD1_MASK, "popdown", 0);
+       gtk_binding_entry_add_signal (
+               gtk_binding_set_by_class (class),
+               GDK_KEY_KP_Up, GDK_MOD1_MASK, "popdown", 0);
+       gtk_binding_entry_add_signal (
+               gtk_binding_set_by_class (class),
+               GDK_KEY_Escape, 0, "popdown", 0);
+}
+
+static void
+e_color_combo_init (EColorCombo *combo)
+{
+       GtkWidget *container;
+       GtkWidget *toplevel;
+       GtkWidget *widget;
+       GList *palette;
+       guint ii;
+
+       combo->priv = E_COLOR_COMBO_GET_PRIVATE (combo);
+
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+       gtk_container_add (GTK_CONTAINER (combo), widget);
+
+       container = widget;
+
+       /* Build the combo button. */
+       widget = gtk_frame_new (NULL);
+       gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+       g_signal_connect (
+               widget, "draw",
+               G_CALLBACK (color_combo_draw_frame_cb), combo);
+       combo->priv->color_frame = widget;  /* do not reference */
+
+       widget = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
+
+       widget = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
+       combo->priv->arrow = widget;  /* do not reference */
+
+       /* Build the drop-down menu */
+       widget = gtk_window_new (GTK_WINDOW_POPUP);
+       gtk_container_set_border_width (GTK_CONTAINER (widget), 5);
+       gtk_window_set_resizable (GTK_WINDOW (widget), FALSE);
+       gtk_window_set_type_hint (
+               GTK_WINDOW (widget), GDK_WINDOW_TYPE_HINT_COMBO);
+       combo->priv->window = g_object_ref_sink (widget);
+
+       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo));
+       if (GTK_IS_WINDOW (toplevel)) {
+               gtk_window_group_add_window (
+                       gtk_window_get_group (GTK_WINDOW (toplevel)),
+                       GTK_WINDOW (widget));
+               gtk_window_set_transient_for (
+                       GTK_WINDOW (widget), GTK_WINDOW (toplevel));
+       }
+
+       g_signal_connect_swapped (
+               widget, "show",
+               G_CALLBACK (color_combo_child_show_cb), combo);
+       g_signal_connect_swapped (
+               widget, "hide",
+               G_CALLBACK (color_combo_child_hide_cb), combo);
+       g_signal_connect_swapped (
+               widget, "button-press-event",
+               G_CALLBACK (color_combo_window_button_press_event_cb), combo);
+       g_signal_connect_swapped (
+               widget, "button-release-event",
+               G_CALLBACK (color_combo_window_button_release_event_cb), combo);
+
+       container = widget;
+
+       widget = gtk_grid_new ();
+       gtk_grid_set_row_spacing (GTK_GRID (widget), 5);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+
+       container = widget;
+
+       widget = gtk_button_new ();
+       gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 1);
+       combo->priv->default_button = widget;  /* do not reference */
+
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (color_combo_set_default_color_cb), combo);
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (color_combo_popdown), combo);
+
+       widget = e_color_chooser_widget_new ();
+       g_object_set_data (G_OBJECT (widget), "window", combo->priv->window);
+       gtk_grid_attach (GTK_GRID (container), widget, 0, 1, 1, 1);
+       combo->priv->chooser_widget = widget;  /* do not reference */
+
+       g_signal_connect_swapped (
+               widget, "color-activated",
+               G_CALLBACK (color_combo_swatch_color_changed), combo);
+       g_signal_connect_swapped (
+               widget, "editor-activated",
+               G_CALLBACK (color_combo_popdown), combo);
+
+       palette = NULL;
+       for (ii = 0; ii < G_N_ELEMENTS (default_colors); ii++) {
+               GdkRGBA *color = g_new0 (GdkRGBA, 1);
+               gdk_rgba_parse (color, default_colors[ii].color);
+
+               palette = g_list_prepend (palette, color);
+       }
+       palette = g_list_reverse (palette);
+       e_color_combo_set_palette (combo, palette);
+       g_list_free_full (palette, (GDestroyNotify) g_free);
+
+       combo->priv->current_color = gdk_rgba_copy (&black);
+       combo->priv->default_color = gdk_rgba_copy (&black);
+}
+
+GtkWidget *
+e_color_combo_new (void)
+{
+       return g_object_new (E_TYPE_COLOR_COMBO, NULL);
+}
+
+GtkWidget *
+e_color_combo_new_defaults (GdkRGBA *default_color,
+                            const gchar *default_label)
+{
+       g_return_val_if_fail (default_color != NULL, NULL);
+       g_return_val_if_fail (default_label != NULL, NULL);
+
+       return g_object_new (
+               E_TYPE_COLOR_COMBO,
+               "default-color", default_color,
+               "default-label", default_label,
+               NULL);
+}
+
+void
+e_color_combo_popup (EColorCombo *combo)
+{
+       g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+       g_signal_emit (combo, signals[POPUP], 0);
+}
+
+void
+e_color_combo_popdown (EColorCombo *combo)
+{
+       g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+       g_signal_emit (combo, signals[POPDOWN], 0);
+}
+
+void
+e_color_combo_get_current_color (EColorCombo *combo,
+                                 GdkRGBA *color)
+{
+       g_return_if_fail (E_IS_COLOR_COMBO (combo));
+       g_return_if_fail (color != NULL);
+
+       color->red = combo->priv->current_color->red;
+       color->green = combo->priv->current_color->green;
+       color->blue = combo->priv->current_color->blue;
+       color->alpha = combo->priv->current_color->alpha;
+}
+
+void
+e_color_combo_set_current_color (EColorCombo *combo,
+                                 const GdkRGBA *color)
+{
+       g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+       if (color == NULL)
+               color = &black;
+
+       if (combo->priv->current_color) {
+
+               if (gdk_rgba_equal (color, combo->priv->current_color)) {
+                       return;
+               }
+
+               gdk_rgba_free (combo->priv->current_color);
+       }
+
+       combo->priv->current_color = gdk_rgba_copy (color);
+
+       gtk_color_chooser_set_rgba (
+               GTK_COLOR_CHOOSER (combo->priv->chooser_widget), color);
+       gtk_widget_queue_draw (combo->priv->color_frame);
+
+       g_object_notify (G_OBJECT (combo), "current-color");
+}
+
+void
+e_color_combo_get_default_color (EColorCombo *combo,
+                                 GdkRGBA *color)
+{
+       g_return_if_fail (E_IS_COLOR_COMBO (combo));
+       g_return_if_fail (color != NULL);
+
+       color->red = combo->priv->default_color->red;
+       color->green = combo->priv->default_color->green;
+       color->blue = combo->priv->default_color->blue;
+       color->alpha = combo->priv->default_color->alpha;
+}
+
+void
+e_color_combo_set_default_color (EColorCombo *combo,
+                                 const GdkRGBA *color)
+{
+       g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+       if (color == NULL)
+               color = &black;
+
+       if (combo->priv->default_color) {
+
+               if (gdk_rgba_equal (color, combo->priv->default_color)) {
+                       return;
+               }
+
+               gdk_rgba_free (combo->priv->default_color);
+       }
+       combo->priv->default_color = gdk_rgba_copy (color);
+
+       gtk_color_chooser_set_rgba (
+               GTK_COLOR_CHOOSER (combo->priv->chooser_widget), color);
+
+       g_object_notify (G_OBJECT (combo), "default-color");
+}
+
+const gchar *
+e_color_combo_get_default_label (EColorCombo *combo)
+{
+       g_return_val_if_fail (E_IS_COLOR_COMBO (combo), NULL);
+
+       return gtk_button_get_label (GTK_BUTTON (combo->priv->default_button));
+}
+
+void
+e_color_combo_set_default_label (EColorCombo *combo,
+                                 const gchar *text)
+{
+       g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+       gtk_button_set_label (GTK_BUTTON (combo->priv->default_button), text);
+
+       g_object_notify (G_OBJECT (combo), "default-label");
+}
+
+gboolean
+e_color_combo_get_default_transparent (EColorCombo *combo)
+{
+       g_return_val_if_fail (E_IS_COLOR_COMBO (combo), FALSE);
+
+       return combo->priv->default_transparent;
+}
+
+void
+e_color_combo_set_default_transparent (EColorCombo *combo,
+                                       gboolean transparent)
+{
+       g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+       combo->priv->default_transparent = transparent;
+
+       g_object_notify (G_OBJECT (combo), "default-transparent");
+}
+
+GList *
+e_color_combo_get_palette (EColorCombo *combo)
+{
+       g_return_val_if_fail (E_IS_COLOR_COMBO (combo), NULL);
+
+       return g_list_copy (combo->priv->palette);
+}
+
+void
+e_color_combo_set_palette (EColorCombo *combo,
+                           GList *palette)
+{
+       gint ii, count, colors_per_line;
+       GList *iter;
+       GdkRGBA *colors;
+
+       g_return_if_fail (E_IS_COLOR_COMBO (combo));
+
+       count = g_list_length (palette);
+       colors_per_line = (count % 10 == 0) ? 10 : 9;
+
+       colors = g_malloc_n (count, sizeof (GdkRGBA));
+       g_list_free_full (combo->priv->palette, (GDestroyNotify) gdk_rgba_free);
+       ii = 0;
+       combo->priv->palette = NULL;
+       for (iter = palette; iter; iter = g_list_next (iter)) {
+               combo->priv->palette = g_list_prepend (
+                       combo->priv->palette, gdk_rgba_copy (iter->data));
+
+               colors[ii] = *((GdkRGBA *) iter->data);
+               ii++;
+       }
+       combo->priv->palette = g_list_reverse (combo->priv->palette);
+
+       gtk_color_chooser_add_palette (
+               GTK_COLOR_CHOOSER (combo->priv->chooser_widget),
+               GTK_ORIENTATION_HORIZONTAL, 0, 0, NULL);
+       gtk_color_chooser_add_palette (
+               GTK_COLOR_CHOOSER (combo->priv->chooser_widget),
+               GTK_ORIENTATION_HORIZONTAL, colors_per_line, count, colors);
+       g_free (colors);
+}
diff --git a/e-util/e-color-combo.h b/e-util/e-color-combo.h
new file mode 100644
index 0000000..41f7fd1
--- /dev/null
+++ b/e-util/e-color-combo.h
@@ -0,0 +1,96 @@
+/* e-color-combo.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_COLOR_COMBO_H
+#define E_COLOR_COMBO_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_COLOR_COMBO \
+       (e_color_combo_get_type ())
+#define E_COLOR_COMBO(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COLOR_COMBO, EColorCombo))
+#define E_COLOR_COMBO_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COLOR_COMBO, EColorComboClass))
+#define E_IS_COLOR_COMBO(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COLOR_COMBO))
+#define E_IS_COLOR_COMBO_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COLOR_COMBO))
+#define E_COLOR_COMBO_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COLOR_COMBO, EColorComboClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EColorCombo EColorCombo;
+typedef struct _EColorComboClass EColorComboClass;
+typedef struct _EColorComboPrivate EColorComboPrivate;
+
+struct _EColorCombo {
+       GtkButton parent;
+       EColorComboPrivate *priv;
+};
+
+struct _EColorComboClass {
+       GtkButtonClass parent_class;
+
+       void            (*popup)                (EColorCombo *combo);
+       void            (*popdown)              (EColorCombo *combo);
+       void            (*activated)            (EColorCombo *combo,
+                                                GdkRGBA *color);
+};
+
+GType          e_color_combo_get_type          (void) G_GNUC_CONST;
+GtkWidget *    e_color_combo_new               (void);
+GtkWidget *    e_color_combo_new_defaults      (GdkRGBA *default_color,
+                                                const gchar *default_label);
+void           e_color_combo_popup             (EColorCombo *combo);
+void           e_color_combo_popdown           (EColorCombo *combo);
+void           e_color_combo_get_current_color (EColorCombo *combo,
+                                                GdkRGBA *rgba);
+void           e_color_combo_set_current_color (EColorCombo *combo,
+                                                const GdkRGBA *color);
+void           e_color_combo_get_default_color (EColorCombo *combo,
+                                                GdkRGBA *color);
+void           e_color_combo_set_default_color (EColorCombo *combo,
+                                                const GdkRGBA *default_color);
+const gchar *  e_color_combo_get_default_label (EColorCombo *combo);
+void           e_color_combo_set_default_label (EColorCombo *combo,
+                                                const gchar *text);
+gboolean       e_color_combo_get_default_transparent
+                                               (EColorCombo *combo);
+void           e_color_combo_set_default_transparent
+                                               (EColorCombo *combo,
+                                                gboolean transparent);
+GList  *       e_color_combo_get_palette       (EColorCombo *combo);
+void           e_color_combo_set_palette       (EColorCombo *combo,
+                                                GList *palette);
+
+G_END_DECLS
+
+#endif /* E_COLOR_COMBO_H */
diff --git a/e-util/e-emoticon-action.c b/e-util/e-emoticon-action.c
new file mode 100644
index 0000000..0850d33
--- /dev/null
+++ b/e-util/e-emoticon-action.c
@@ -0,0 +1,278 @@
+/*
+ * e-emoticon-action.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "e-emoticon-action.h"
+
+#include "e-emoticon-chooser.h"
+#include "e-emoticon-chooser-menu.h"
+#include "e-emoticon-tool-button.h"
+
+#define E_EMOTICON_ACTION_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_EMOTICON_ACTION, EEmoticonActionPrivate))
+
+struct _EEmoticonActionPrivate {
+       GList *choosers;
+       EEmoticonChooser *current_chooser;
+};
+
+enum {
+       PROP_0,
+       PROP_CURRENT_FACE
+};
+
+/* Forward Declarations */
+static void    e_emoticon_action_interface_init
+                                       (EEmoticonChooserInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+       EEmoticonAction,
+       e_emoticon_action,
+       GTK_TYPE_ACTION,
+       G_IMPLEMENT_INTERFACE (
+               E_TYPE_EMOTICON_CHOOSER,
+               e_emoticon_action_interface_init))
+
+static void
+emoticon_action_proxy_item_activated_cb (EEmoticonAction *action,
+                                         EEmoticonChooser *chooser)
+{
+       action->priv->current_chooser = chooser;
+
+       g_signal_emit_by_name (action, "item-activated");
+}
+
+static void
+emoticon_action_set_property (GObject *object,
+                              guint property_id,
+                              const GValue *value,
+                              GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CURRENT_FACE:
+                       e_emoticon_chooser_set_current_emoticon (
+                               E_EMOTICON_CHOOSER (object),
+                               g_value_get_boxed (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_action_get_property (GObject *object,
+                              guint property_id,
+                              GValue *value,
+                              GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CURRENT_FACE:
+                       g_value_set_boxed (
+                               value, e_emoticon_chooser_get_current_emoticon (
+                               E_EMOTICON_CHOOSER (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_action_finalize (GObject *object)
+{
+       EEmoticonActionPrivate *priv;
+
+       priv = E_EMOTICON_ACTION_GET_PRIVATE (object);
+
+       g_list_free (priv->choosers);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_emoticon_action_parent_class)->finalize (object);
+}
+
+static void
+emoticon_action_activate (GtkAction *action)
+{
+       EEmoticonActionPrivate *priv;
+
+       priv = E_EMOTICON_ACTION_GET_PRIVATE (action);
+
+       priv->current_chooser = NULL;
+}
+
+static GtkWidget *
+emoticon_action_create_menu_item (GtkAction *action)
+{
+       GtkWidget *item;
+       GtkWidget *menu;
+
+       item = gtk_image_menu_item_new ();
+       menu = gtk_action_create_menu (action);
+       gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
+       gtk_widget_show (menu);
+
+       return item;
+}
+
+static GtkWidget *
+emoticon_action_create_tool_item (GtkAction *action)
+{
+       return GTK_WIDGET (e_emoticon_tool_button_new ());
+}
+
+static void
+emoticon_action_connect_proxy (GtkAction *action,
+                               GtkWidget *proxy)
+{
+       EEmoticonActionPrivate *priv;
+
+       priv = E_EMOTICON_ACTION_GET_PRIVATE (action);
+
+       if (!E_IS_EMOTICON_CHOOSER (proxy))
+               goto chainup;
+
+       if (g_list_find (priv->choosers, proxy) != NULL)
+               goto chainup;
+
+       g_signal_connect_swapped (
+               proxy, "item-activated",
+               G_CALLBACK (emoticon_action_proxy_item_activated_cb), action);
+
+chainup:
+       /* Chain up to parent's connect_proxy() method. */
+       GTK_ACTION_CLASS (e_emoticon_action_parent_class)->
+               connect_proxy (action, proxy);
+}
+
+static void
+emoticon_action_disconnect_proxy (GtkAction *action,
+                                  GtkWidget *proxy)
+{
+       EEmoticonActionPrivate *priv;
+
+       priv = E_EMOTICON_ACTION_GET_PRIVATE (action);
+
+       priv->choosers = g_list_remove (priv->choosers, proxy);
+
+       /* Chain up to parent's disconnect_proxy() method. */
+       GTK_ACTION_CLASS (e_emoticon_action_parent_class)->
+               disconnect_proxy (action, proxy);
+}
+
+static GtkWidget *
+emoticon_action_create_menu (GtkAction *action)
+{
+       EEmoticonActionPrivate *priv;
+       GtkWidget *widget;
+
+       priv = E_EMOTICON_ACTION_GET_PRIVATE (action);
+
+       widget = e_emoticon_chooser_menu_new ();
+
+       g_signal_connect_swapped (
+               widget, "item-activated",
+               G_CALLBACK (emoticon_action_proxy_item_activated_cb), action);
+
+       priv->choosers = g_list_prepend (priv->choosers, widget);
+
+       return widget;
+}
+
+static EEmoticon *
+emoticon_action_get_current_emoticon (EEmoticonChooser *chooser)
+{
+       EEmoticonActionPrivate *priv;
+       EEmoticon *emoticon = NULL;
+
+       priv = E_EMOTICON_ACTION_GET_PRIVATE (chooser);
+
+       if (priv->current_chooser != NULL)
+               emoticon = e_emoticon_chooser_get_current_emoticon (
+                       priv->current_chooser);
+
+       return emoticon;
+}
+
+static void
+emoticon_action_set_current_emoticon (EEmoticonChooser *chooser,
+                                      EEmoticon *emoticon)
+{
+       EEmoticonActionPrivate *priv;
+       GList *iter;
+
+       priv = E_EMOTICON_ACTION_GET_PRIVATE (chooser);
+
+       for (iter = priv->choosers; iter != NULL; iter = iter->next) {
+               EEmoticonChooser *proxy_chooser = iter->data;
+
+               e_emoticon_chooser_set_current_emoticon (proxy_chooser, emoticon);
+       }
+}
+
+static void
+e_emoticon_action_class_init (EEmoticonActionClass *class)
+{
+       GObjectClass *object_class;
+       GtkActionClass *action_class;
+
+       g_type_class_add_private (class, sizeof (EEmoticonAction));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = emoticon_action_set_property;
+       object_class->get_property = emoticon_action_get_property;
+       object_class->finalize = emoticon_action_finalize;
+
+       action_class = GTK_ACTION_CLASS (class);
+       action_class->activate = emoticon_action_activate;
+       action_class->create_menu_item = emoticon_action_create_menu_item;
+       action_class->create_tool_item = emoticon_action_create_tool_item;
+       action_class->connect_proxy = emoticon_action_connect_proxy;
+       action_class->disconnect_proxy = emoticon_action_disconnect_proxy;
+       action_class->create_menu = emoticon_action_create_menu;
+
+       g_object_class_override_property (
+               object_class, PROP_CURRENT_FACE, "current-emoticon");
+}
+
+static void
+e_emoticon_action_interface_init (EEmoticonChooserInterface *interface)
+{
+       interface->get_current_emoticon = emoticon_action_get_current_emoticon;
+       interface->set_current_emoticon = emoticon_action_set_current_emoticon;
+}
+
+static void
+e_emoticon_action_init (EEmoticonAction *action)
+{
+       action->priv = E_EMOTICON_ACTION_GET_PRIVATE (action);
+}
+
+GtkAction *
+e_emoticon_action_new (const gchar *name,
+                       const gchar *label,
+                       const gchar *tooltip,
+                       const gchar *stock_id)
+{
+       g_return_val_if_fail (name != NULL, NULL);
+
+       return g_object_new (
+               E_TYPE_EMOTICON_ACTION, "name", name, "label", label,
+               "tooltip", tooltip, "stock-id", stock_id, NULL);
+}
diff --git a/e-util/e-emoticon-action.h b/e-util/e-emoticon-action.h
new file mode 100644
index 0000000..0e450e8
--- /dev/null
+++ b/e-util/e-emoticon-action.h
@@ -0,0 +1,73 @@
+/*
+ * e-emoticon-action.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_ACTION_H
+#define E_EMOTICON_ACTION_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_ACTION \
+       (e_emoticon_action_get_type ())
+#define E_EMOTICON_ACTION(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_EMOTICON_ACTION, EEmoticonAction))
+#define E_EMOTICON_ACTION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_EMOTICON_ACTION, EEmoticonActionClass))
+#define E_IS_EMOTICON_ACTION(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_EMOTICON_ACTION))
+#define E_IS_EMOTICON_ACTION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_EMOTICON_ACTION))
+#define E_EMOTICON_ACTION_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_EMOTICON_ACTION, EEmoticonActionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonAction EEmoticonAction;
+typedef struct _EEmoticonActionClass EEmoticonActionClass;
+typedef struct _EEmoticonActionPrivate EEmoticonActionPrivate;
+
+struct _EEmoticonAction {
+       GtkAction parent;
+       EEmoticonActionPrivate *priv;
+};
+
+struct _EEmoticonActionClass {
+       GtkActionClass parent_class;
+};
+
+GType          e_emoticon_action_get_type      (void) G_GNUC_CONST;
+GtkAction *    e_emoticon_action_new           (const gchar *name,
+                                                const gchar *label,
+                                                const gchar *tooltip,
+                                                const gchar *stock_id);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_ACTION_H */
diff --git a/e-util/e-emoticon-chooser-menu.c b/e-util/e-emoticon-chooser-menu.c
new file mode 100644
index 0000000..f2ed337
--- /dev/null
+++ b/e-util/e-emoticon-chooser-menu.c
@@ -0,0 +1,184 @@
+/*
+ * e-emoticon-chooser-menu.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-emoticon-chooser-menu.h"
+#include "e-emoticon-chooser.h"
+
+#include <glib/gi18n-lib.h>
+
+enum {
+       PROP_0,
+       PROP_CURRENT_FACE
+};
+
+/* Forward Declarations */
+static void    e_emoticon_chooser_menu_interface_init
+                                       (EEmoticonChooserInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+       EEmoticonChooserMenu,
+       e_emoticon_chooser_menu,
+       GTK_TYPE_MENU,
+       G_IMPLEMENT_INTERFACE (
+               E_TYPE_EMOTICON_CHOOSER,
+               e_emoticon_chooser_menu_interface_init))
+
+static void
+emoticon_chooser_menu_set_property (GObject *object,
+                                    guint property_id,
+                                    const GValue *value,
+                                    GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CURRENT_FACE:
+                       e_emoticon_chooser_set_current_emoticon (
+                               E_EMOTICON_CHOOSER (object),
+                               g_value_get_boxed (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_chooser_menu_get_property (GObject *object,
+                                    guint property_id,
+                                    GValue *value,
+                                    GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CURRENT_FACE:
+                       g_value_set_boxed (
+                               value,
+                               e_emoticon_chooser_get_current_emoticon (
+                               E_EMOTICON_CHOOSER (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static EEmoticon *
+emoticon_chooser_menu_get_current_emoticon (EEmoticonChooser *chooser)
+{
+       GtkWidget *item;
+
+       item = gtk_menu_get_active (GTK_MENU (chooser));
+       if (item == NULL)
+               return NULL;
+
+       return g_object_get_data (G_OBJECT (item), "emoticon");
+}
+
+static void
+emoticon_chooser_menu_set_current_emoticon (EEmoticonChooser *chooser,
+                                            EEmoticon *emoticon)
+{
+       GList *list, *iter;
+
+       list = gtk_container_get_children (GTK_CONTAINER (chooser));
+
+       for (iter = list; iter != NULL; iter = iter->next) {
+               GtkWidget *item = iter->data;
+               EEmoticon *candidate;
+
+               candidate = g_object_get_data (G_OBJECT (item), "emoticon");
+               if (candidate == NULL)
+                       continue;
+
+               if (e_emoticon_equal (emoticon, candidate)) {
+                       gtk_menu_shell_activate_item (
+                               GTK_MENU_SHELL (chooser), item, TRUE);
+                       break;
+               }
+       }
+
+       g_list_free (list);
+}
+
+static void
+e_emoticon_chooser_menu_class_init (EEmoticonChooserMenuClass *class)
+{
+       GObjectClass *object_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = emoticon_chooser_menu_set_property;
+       object_class->get_property = emoticon_chooser_menu_get_property;
+
+       g_object_class_override_property (
+               object_class, PROP_CURRENT_FACE, "current-emoticon");
+}
+
+static void
+e_emoticon_chooser_menu_interface_init (EEmoticonChooserInterface *interface)
+{
+       interface->get_current_emoticon =
+               emoticon_chooser_menu_get_current_emoticon;
+       interface->set_current_emoticon =
+               emoticon_chooser_menu_set_current_emoticon;
+}
+
+static void
+e_emoticon_chooser_menu_init (EEmoticonChooserMenu *chooser_menu)
+{
+       EEmoticonChooser *chooser;
+       GList *list, *iter;
+
+       chooser = E_EMOTICON_CHOOSER (chooser_menu);
+       list = e_emoticon_chooser_get_items ();
+
+       for (iter = list; iter != NULL; iter = iter->next) {
+               EEmoticon *emoticon = iter->data;
+               GtkWidget *item;
+
+               /* To keep translated strings in subclasses */
+               item = gtk_image_menu_item_new_with_mnemonic (_(emoticon->label));
+               gtk_image_menu_item_set_image (
+                       GTK_IMAGE_MENU_ITEM (item),
+                       gtk_image_new_from_icon_name (
+                       emoticon->icon_name, GTK_ICON_SIZE_MENU));
+               gtk_widget_show (item);
+
+               g_object_set_data_full (
+                       G_OBJECT (item), "emoticon",
+                       e_emoticon_copy (emoticon),
+                       (GDestroyNotify) e_emoticon_free);
+
+               g_signal_connect_swapped (
+                       item, "activate",
+                       G_CALLBACK (e_emoticon_chooser_item_activated),
+                       chooser);
+
+               gtk_menu_shell_append (GTK_MENU_SHELL (chooser_menu), item);
+       }
+
+       g_list_free (list);
+}
+
+GtkWidget *
+e_emoticon_chooser_menu_new (void)
+{
+       return g_object_new (E_TYPE_EMOTICON_CHOOSER_MENU, NULL);
+}
diff --git a/e-util/e-emoticon-chooser-menu.h b/e-util/e-emoticon-chooser-menu.h
new file mode 100644
index 0000000..d4f9954
--- /dev/null
+++ b/e-util/e-emoticon-chooser-menu.h
@@ -0,0 +1,70 @@
+/*
+ * e-emoticon-chooser-menu.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_CHOOSER_MENU_H
+#define E_EMOTICON_CHOOSER_MENU_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_CHOOSER_MENU \
+       (e_emoticon_chooser_menu_get_type ())
+#define E_EMOTICON_CHOOSER_MENU(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_EMOTICON_CHOOSER_MENU, EEmoticonChooserMenu))
+#define E_EMOTICON_CHOOSER_MENU_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_EMOTICON_CHOOSER_MENU, EEmoticonChooserMenuClass))
+#define E_IS_EMOTICON_CHOOSER_MENU(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_EMOTICON_CHOOSER_MENU))
+#define E_IS_EMOTICON_CHOOSER_MENU_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_EMOTICON_CHOOSER_MENU))
+#define E_EMOTICON_CHOOSER_MENU_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_EMOTICON_CHOOSER_MENU, EEmoticonChooserMenuClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonChooserMenu EEmoticonChooserMenu;
+typedef struct _EEmoticonChooserMenuClass EEmoticonChooserMenuClass;
+typedef struct _EEmoticonChooserMenuPrivate EEmoticonChooserMenuPrivate;
+
+struct _EEmoticonChooserMenu {
+       GtkMenu parent;
+};
+
+struct _EEmoticonChooserMenuClass {
+       GtkMenuClass parent_class;
+};
+
+GType          e_emoticon_chooser_menu_get_type
+                                               (void) G_GNUC_CONST;
+GtkWidget *    e_emoticon_chooser_menu_new     (void);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_CHOOSER_MENU_H */
diff --git a/e-util/e-emoticon-chooser.c b/e-util/e-emoticon-chooser.c
new file mode 100644
index 0000000..44ce06b
--- /dev/null
+++ b/e-util/e-emoticon-chooser.c
@@ -0,0 +1,178 @@
+/*
+ * e-emoticon-chooser.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-emoticon-chooser.h"
+
+#include <glib/gi18n-lib.h>
+
+/* Constant version of EEMoticon. */
+typedef struct {
+       const gchar *label;
+       const gchar *icon_name;
+       const gchar *text_face;
+} ConstantEmoticon;
+
+static ConstantEmoticon available_emoticons[] = {
+       /* Translators: :-) */
+       { N_("_Smile"),         "face-smile",           ":-)"   },
+       /* Translators: :-( */
+       { N_("S_ad"),           "face-sad",             ":-("   },
+       /* Translators: ;-) */
+       { N_("_Wink"),          "face-wink",            ";-)"   },
+       /* Translators: :-P */
+       { N_("Ton_gue"),        "face-raspberry",       ":-P"   },
+       /* Translators: :-)) */
+       { N_("Laug_h"),         "face-laugh",           ":-))"  },
+       /* Translators: :-| */
+       { N_("_Plain"),         "face-plain",           ":-|"   },
+       /* Translators: :-! */
+       { N_("Smi_rk"),         "face-smirk",           ":-!"   },
+       /* Translators: :"-) */
+       { N_("_Embarrassed"),   "face-embarrassed",     ":\"-)" },
+       /* Translators: :-D */
+       { N_("_Big Smile"),     "face-smile-big",       ":-D"   },
+       /* Translators: :-/ */
+       { N_("Uncer_tain"),     "face-uncertain",       ":-/"   },
+       /* Translators: :-O */
+       { N_("S_urprise"),      "face-surprise",        ":-O"   },
+       /* Translators: :-S */
+       { N_("W_orried"),       "face-worried",         ":-S"   },
+       /* Translators: :-* */
+       { N_("_Kiss"),          "face-kiss",            ":-*"   },
+       /* Translators: X-( */
+       { N_("A_ngry"),         "face-angry",           "X-("   },
+       /* Translators: B-) */
+       { N_("_Cool"),          "face-cool",            "B-)"   },
+       /* Translators: O:-) */
+       { N_("Ange_l"),         "face-angel",           "O:-)"  },
+       /* Translators: :'( */
+       { N_("Cr_ying"),        "face-crying",          ":'("   },
+       /* Translators: :-Q */
+       { N_("S_ick"),          "face-sick",            ":-Q"   },
+       /* Translators: |-) */
+       { N_("Tire_d"),         "face-tired",           "|-)"   },
+       /* Translators: >:-) */
+       { N_("De_vilish"),      "face-devilish",        ">:-)"  },
+       /* Translators: :-(|) */
+       { N_("_Monkey"),        "face-monkey",          ":-(|)" }
+};
+
+enum {
+       ITEM_ACTIVATED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_INTERFACE (
+       EEmoticonChooser,
+       e_emoticon_chooser,
+       G_TYPE_OBJECT)
+
+static void
+e_emoticon_chooser_default_init (EEmoticonChooserInterface *interface)
+{
+       g_object_interface_install_property (
+               interface,
+               g_param_spec_boxed (
+                       "current-emoticon",
+                       "Current Emoticon",
+                       "Currently selected emoticon",
+                       E_TYPE_EMOTICON,
+                       G_PARAM_READWRITE));
+
+       signals[ITEM_ACTIVATED] = g_signal_new (
+               "item-activated",
+               G_TYPE_FROM_INTERFACE (interface),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EEmoticonChooserInterface, item_activated),
+               NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+}
+
+EEmoticon *
+e_emoticon_chooser_get_current_emoticon (EEmoticonChooser *chooser)
+{
+       EEmoticonChooserInterface *interface;
+
+       g_return_val_if_fail (E_IS_EMOTICON_CHOOSER (chooser), NULL);
+
+       interface = E_EMOTICON_CHOOSER_GET_INTERFACE (chooser);
+       g_return_val_if_fail (interface->get_current_emoticon != NULL, NULL);
+
+       return interface->get_current_emoticon (chooser);
+}
+
+void
+e_emoticon_chooser_set_current_emoticon (EEmoticonChooser *chooser,
+                                         EEmoticon *emoticon)
+{
+       EEmoticonChooserInterface *interface;
+
+       g_return_if_fail (E_IS_EMOTICON_CHOOSER (chooser));
+
+       interface = E_EMOTICON_CHOOSER_GET_INTERFACE (chooser);
+       g_return_if_fail (interface->set_current_emoticon != NULL);
+
+       interface->set_current_emoticon (chooser, emoticon);
+}
+
+void
+e_emoticon_chooser_item_activated (EEmoticonChooser *chooser)
+{
+       g_return_if_fail (E_IS_EMOTICON_CHOOSER (chooser));
+
+       g_signal_emit (chooser, signals[ITEM_ACTIVATED], 0);
+}
+
+GList *
+e_emoticon_chooser_get_items (void)
+{
+       GList *list = NULL;
+       gint ii;
+
+       for (ii = 0; ii < G_N_ELEMENTS (available_emoticons); ii++)
+               list = g_list_prepend (list, &available_emoticons[ii]);
+
+       return g_list_reverse (list);
+}
+
+const EEmoticon *
+e_emoticon_chooser_lookup_emoticon (const gchar *icon_name)
+{
+       gint ii;
+
+       g_return_val_if_fail (icon_name && *icon_name, NULL);
+
+       for (ii = 0; ii < G_N_ELEMENTS (available_emoticons); ii++) {
+               if (strcmp (available_emoticons[ii].icon_name, icon_name) == 0) {
+                       return (const EEmoticon *) &available_emoticons[ii];
+               }
+       }
+
+       return NULL;
+}
+
diff --git a/e-util/e-emoticon-chooser.h b/e-util/e-emoticon-chooser.h
new file mode 100644
index 0000000..14e899f
--- /dev/null
+++ b/e-util/e-emoticon-chooser.h
@@ -0,0 +1,77 @@
+/*
+ * e-emoticon-chooser.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_CHOOSER_H
+#define E_EMOTICON_CHOOSER_H
+
+#include <e-util/e-emoticon.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_CHOOSER \
+       (e_emoticon_chooser_get_type ())
+#define E_EMOTICON_CHOOSER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_EMOTICON_CHOOSER, EEmoticonChooser))
+#define E_IS_EMOTICON_CHOOSER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_EMOTICON_CHOOSER))
+#define E_EMOTICON_CHOOSER_GET_INTERFACE(obj) \
+       (G_TYPE_INSTANCE_GET_INTERFACE \
+       ((obj), E_TYPE_EMOTICON_CHOOSER, EEmoticonChooserInterface))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonChooser EEmoticonChooser;
+typedef struct _EEmoticonChooserInterface EEmoticonChooserInterface;
+
+struct _EEmoticonChooserInterface {
+       GTypeInterface parent_interface;
+
+       /* Methods */
+       EEmoticon *     (*get_current_emoticon) (EEmoticonChooser *chooser);
+       void            (*set_current_emoticon) (EEmoticonChooser *chooser,
+                                                EEmoticon *emoticon);
+
+       /* Signals */
+       void            (*item_activated)       (EEmoticonChooser *chooser);
+};
+
+GType          e_emoticon_chooser_get_type     (void) G_GNUC_CONST;
+EEmoticon *    e_emoticon_chooser_get_current_emoticon
+                                               (EEmoticonChooser *chooser);
+void           e_emoticon_chooser_set_current_emoticon
+                                               (EEmoticonChooser *chooser,
+                                                EEmoticon *emoticon);
+void           e_emoticon_chooser_item_activated
+                                               (EEmoticonChooser *chooser);
+
+GList *                e_emoticon_chooser_get_items    (void);
+const EEmoticon *
+               e_emoticon_chooser_lookup_emoticon
+                                               (const gchar *icon_name);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_CHOOSER_H */
diff --git a/e-util/e-emoticon-tool-button.c b/e-util/e-emoticon-tool-button.c
new file mode 100644
index 0000000..54f99c9
--- /dev/null
+++ b/e-util/e-emoticon-tool-button.c
@@ -0,0 +1,695 @@
+/*
+ * e-emoticon-tool-button.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-emoticon-tool-button.h"
+
+/* XXX The "button" aspects of this widget are based heavily on the
+ *     GtkComboBox tree-view implementation.  Consider splitting it
+ *     into a reusable "button-with-an-empty-window" widget. */
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "e-emoticon-chooser.h"
+
+#define E_EMOTICON_TOOL_BUTTON_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButtonPrivate))
+
+/* XXX Should calculate this dynamically. */
+#define NUM_ROWS       7
+#define NUM_COLS       3
+
+enum {
+       PROP_0,
+       PROP_CURRENT_EMOTICON,
+       PROP_POPUP_SHOWN
+};
+
+enum {
+       POPUP,
+       POPDOWN,
+       LAST_SIGNAL
+};
+
+struct _EEmoticonToolButtonPrivate {
+       GtkWidget *active_button;  /* not referenced */
+       GtkWidget *table;
+       GtkWidget *window;
+
+       guint popup_shown       : 1;
+       guint popup_in_progress : 1;
+       GdkDevice *grab_keyboard;
+       GdkDevice *grab_mouse;
+};
+
+static guint signals[LAST_SIGNAL];
+
+/* Forward Declarations */
+static void    e_emoticon_tool_button_interface_init
+                                       (EEmoticonChooserInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+       EEmoticonToolButton,
+       e_emoticon_tool_button,
+       GTK_TYPE_TOGGLE_TOOL_BUTTON,
+       G_IMPLEMENT_INTERFACE (
+               E_TYPE_EMOTICON_CHOOSER,
+               e_emoticon_tool_button_interface_init))
+
+/* XXX Copied from _gtk_toolbar_elide_underscores() */
+static gchar *
+emoticon_tool_button_elide_underscores (const gchar *original)
+{
+       gchar *q, *result;
+       const gchar *p, *end;
+       gsize len;
+       gboolean last_underscore;
+
+       if (!original)
+               return NULL;
+
+       len = strlen (original);
+       q = result = g_malloc (len + 1);
+       last_underscore = FALSE;
+
+       end = original + len;
+       for (p = original; p < end; p++) {
+               if (!last_underscore && *p == '_')
+                       last_underscore = TRUE;
+               else {
+                       last_underscore = FALSE;
+                       if (original + 2 <= p && p + 1 <= end &&
+                               p[-2] == '(' && p[-1] == '_' &&
+                               p[0] != '_' && p[1] == ')') {
+                               q--;
+                               *q = '\0';
+                               p++;
+                       } else
+                               *q++ = *p;
+               }
+       }
+
+       if (last_underscore)
+               *q++ = '_';
+
+       *q = '\0';
+
+       return result;
+}
+
+static void
+emoticon_tool_button_reposition_window (EEmoticonToolButton *button)
+{
+       GdkScreen *screen;
+       GdkWindow *window;
+       GdkRectangle monitor;
+       GtkAllocation allocation;
+       gint monitor_num;
+       gint x, y, width, height;
+
+       screen = gtk_widget_get_screen (GTK_WIDGET (button));
+       window = gtk_widget_get_window (GTK_WIDGET (button));
+       monitor_num = gdk_screen_get_monitor_at_window (screen, window);
+       gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+       gdk_window_get_origin (window, &x, &y);
+
+       if (!gtk_widget_get_has_window (GTK_WIDGET (button))) {
+               gtk_widget_get_allocation (GTK_WIDGET (button), &allocation);
+               x += allocation.x;
+               y += allocation.y;
+       }
+
+       gtk_widget_get_allocation (button->priv->window, &allocation);
+       width = allocation.width;
+       height = allocation.height;
+
+       x = CLAMP (x, monitor.x, monitor.x + monitor.width - width);
+       y = CLAMP (y, monitor.y, monitor.y + monitor.height - height);
+
+       gtk_window_move (GTK_WINDOW (button->priv->window), x, y);
+}
+
+static void
+emoticon_tool_button_emoticon_clicked_cb (EEmoticonToolButton *button,
+                                          GtkWidget *emoticon_button)
+{
+       button->priv->active_button = emoticon_button;
+       e_emoticon_tool_button_popdown (button);
+}
+
+static gboolean
+emoticon_tool_button_emoticon_release_event_cb (EEmoticonToolButton *button,
+                                                GdkEventButton *event,
+                                                GtkButton *emoticon_button)
+{
+       GtkStateType state;
+
+       state = gtk_widget_get_state (GTK_WIDGET (button));
+
+       if (state != GTK_STATE_NORMAL)
+               gtk_button_clicked (emoticon_button);
+
+       return FALSE;
+}
+
+static gboolean
+emoticon_tool_button_button_release_event_cb (EEmoticonToolButton *button,
+                                              GdkEventButton *event)
+{
+       GtkToggleToolButton *tool_button;
+       GtkWidget *event_widget;
+       gboolean popup_in_progress;
+
+       tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
+       event_widget = gtk_get_event_widget ((GdkEvent *) event);
+
+       popup_in_progress = button->priv->popup_in_progress;
+       button->priv->popup_in_progress = FALSE;
+
+       if (event_widget != GTK_WIDGET (button))
+               goto popdown;
+
+       if (popup_in_progress)
+               return FALSE;
+
+       if (gtk_toggle_tool_button_get_active (tool_button))
+               goto popdown;
+
+       return FALSE;
+
+popdown:
+       e_emoticon_tool_button_popdown (button);
+
+       return TRUE;
+}
+
+static void
+emoticon_tool_button_child_show_cb (EEmoticonToolButton *button)
+{
+       button->priv->popup_shown = TRUE;
+       g_object_notify (G_OBJECT (button), "popup-shown");
+}
+
+static void
+emoticon_tool_button_child_hide_cb (EEmoticonToolButton *button)
+{
+       button->priv->popup_shown = FALSE;
+       g_object_notify (G_OBJECT (button), "popup-shown");
+}
+
+static gboolean
+emoticon_tool_button_child_key_press_event_cb (EEmoticonToolButton *button,
+                                               GdkEventKey *event)
+{
+       GtkWidget *window = button->priv->window;
+
+       if (!gtk_bindings_activate_event (G_OBJECT (window), event))
+               gtk_bindings_activate_event (G_OBJECT (button), event);
+
+       return TRUE;
+}
+
+static void
+emoticon_tool_button_set_property (GObject *object,
+                                   guint property_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CURRENT_EMOTICON:
+                       e_emoticon_chooser_set_current_emoticon (
+                               E_EMOTICON_CHOOSER (object),
+                               g_value_get_boxed (value));
+                       return;
+
+               case PROP_POPUP_SHOWN:
+                       if (g_value_get_boolean (value))
+                               e_emoticon_tool_button_popup (
+                                       E_EMOTICON_TOOL_BUTTON (object));
+                       else
+                               e_emoticon_tool_button_popdown (
+                                       E_EMOTICON_TOOL_BUTTON (object));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_tool_button_get_property (GObject *object,
+                                   guint property_id,
+                                   GValue *value,
+                                   GParamSpec *pspec)
+{
+       EEmoticonToolButtonPrivate *priv;
+
+       priv = E_EMOTICON_TOOL_BUTTON_GET_PRIVATE (object);
+
+       switch (property_id) {
+               case PROP_CURRENT_EMOTICON:
+                       g_value_set_boxed (
+                               value,
+                               e_emoticon_chooser_get_current_emoticon (
+                               E_EMOTICON_CHOOSER (object)));
+                       return;
+
+               case PROP_POPUP_SHOWN:
+                       g_value_set_boolean (value, priv->popup_shown);
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_tool_button_dispose (GObject *object)
+{
+       EEmoticonToolButtonPrivate *priv;
+
+       priv = E_EMOTICON_TOOL_BUTTON_GET_PRIVATE (object);
+
+       if (priv->window != NULL) {
+               gtk_widget_destroy (priv->window);
+               priv->window = NULL;
+       }
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_emoticon_tool_button_parent_class)->dispose (object);
+}
+
+static gboolean
+emoticon_tool_button_press_event (GtkWidget *widget,
+                                  GdkEventButton *event)
+{
+       EEmoticonToolButton *button;
+       GtkToggleToolButton *toggle_button;
+       GtkWidget *event_widget;
+
+       button = E_EMOTICON_TOOL_BUTTON (widget);
+
+       event_widget = gtk_get_event_widget ((GdkEvent *) event);
+
+       if (event_widget == button->priv->window)
+               return TRUE;
+
+       if (event_widget != widget)
+               return FALSE;
+
+       toggle_button = GTK_TOGGLE_TOOL_BUTTON (widget);
+       if (gtk_toggle_tool_button_get_active (toggle_button))
+               return FALSE;
+
+       e_emoticon_tool_button_popup (button);
+
+       button->priv->popup_in_progress = TRUE;
+
+       return TRUE;
+}
+
+static void
+emoticon_tool_button_toggled (GtkToggleToolButton *button)
+{
+       if (gtk_toggle_tool_button_get_active (button))
+               e_emoticon_tool_button_popup (
+                       E_EMOTICON_TOOL_BUTTON (button));
+       else
+               e_emoticon_tool_button_popdown (
+                       E_EMOTICON_TOOL_BUTTON (button));
+}
+
+static void
+emoticon_tool_button_popup (EEmoticonToolButton *button)
+{
+       GtkToggleToolButton *tool_button;
+       GdkWindow *window;
+       gboolean grab_status;
+       GdkDevice *device, *mouse, *keyboard;
+       guint32 activate_time;
+
+       device = gtk_get_current_event_device ();
+       g_return_if_fail (device != NULL);
+
+       if (!gtk_widget_get_realized (GTK_WIDGET (button)))
+               return;
+
+       if (button->priv->popup_shown)
+               return;
+
+       activate_time = gtk_get_current_event_time ();
+       if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) {
+               keyboard = device;
+               mouse = gdk_device_get_associated_device (device);
+       } else {
+               keyboard = gdk_device_get_associated_device (device);
+               mouse = device;
+       }
+
+       /* Position the window over the button. */
+       emoticon_tool_button_reposition_window (button);
+
+       /* Show the pop-up. */
+       gtk_widget_show (button->priv->window);
+       gtk_widget_grab_focus (button->priv->window);
+
+       /* Activate the tool button. */
+       tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
+       gtk_toggle_tool_button_set_active (tool_button, TRUE);
+
+       /* Try to grab the pointer and keyboard. */
+       window = gtk_widget_get_window (button->priv->window);
+       grab_status = !keyboard ||
+               gdk_device_grab (
+                       keyboard, window,
+                       GDK_OWNERSHIP_WINDOW, TRUE,
+                       GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+                       NULL, activate_time) == GDK_GRAB_SUCCESS;
+       if (grab_status) {
+               grab_status = !mouse ||
+                       gdk_device_grab (mouse, window,
+                               GDK_OWNERSHIP_WINDOW, TRUE,
+                               GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK,
+                               NULL, activate_time) == GDK_GRAB_SUCCESS;
+               if (!grab_status && keyboard)
+                       gdk_device_ungrab (keyboard, activate_time);
+       }
+
+       if (grab_status) {
+               gtk_device_grab_add (button->priv->window, mouse, TRUE);
+               button->priv->grab_keyboard = keyboard;
+               button->priv->grab_mouse = mouse;
+       } else {
+               gtk_widget_hide (button->priv->window);
+       }
+}
+
+static void
+emoticon_tool_button_popdown (EEmoticonToolButton *button)
+{
+       GtkToggleToolButton *tool_button;
+
+       if (!gtk_widget_get_realized (GTK_WIDGET (button)))
+               return;
+
+       if (!button->priv->popup_shown)
+               return;
+
+       /* Hide the pop-up. */
+       gtk_device_grab_remove (button->priv->window, button->priv->grab_mouse);
+       gtk_widget_hide (button->priv->window);
+
+       /* Deactivate the tool button. */
+       tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
+       gtk_toggle_tool_button_set_active (tool_button, FALSE);
+
+       if (button->priv->grab_keyboard)
+               gdk_device_ungrab (button->priv->grab_keyboard, GDK_CURRENT_TIME);
+       if (button->priv->grab_mouse)
+               gdk_device_ungrab (button->priv->grab_mouse, GDK_CURRENT_TIME);
+
+       button->priv->grab_keyboard = NULL;
+       button->priv->grab_mouse = NULL;
+}
+
+static EEmoticon *
+emoticon_tool_button_get_current_emoticon (EEmoticonChooser *chooser)
+{
+       EEmoticonToolButtonPrivate *priv;
+
+       priv = E_EMOTICON_TOOL_BUTTON_GET_PRIVATE (chooser);
+
+       if (priv->active_button == NULL)
+               return NULL;
+
+       return g_object_get_data (G_OBJECT (priv->active_button), "emoticon");
+}
+
+static void
+emoticon_tool_button_set_current_emoticon (EEmoticonChooser *chooser,
+                                           EEmoticon *emoticon)
+{
+       EEmoticonToolButtonPrivate *priv;
+       GList *list, *iter;
+
+       priv = E_EMOTICON_TOOL_BUTTON_GET_PRIVATE (chooser);
+
+       list = gtk_container_get_children (GTK_CONTAINER (priv->table));
+
+       for (iter = list; iter != NULL; iter = iter->next) {
+               GtkWidget *item = iter->data;
+               EEmoticon *candidate;
+
+               candidate = g_object_get_data (G_OBJECT (item), "emoticon");
+               if (candidate == NULL)
+                       continue;
+
+               if (e_emoticon_equal (emoticon, candidate)) {
+                       gtk_button_clicked (GTK_BUTTON (item));
+                       break;
+               }
+       }
+
+       g_list_free (list);
+}
+
+static void
+e_emoticon_tool_button_class_init (EEmoticonToolButtonClass *class)
+{
+       GObjectClass *object_class;
+       GtkWidgetClass *widget_class;
+       GtkToggleToolButtonClass *toggle_tool_button_class;
+
+       g_type_class_add_private (class, sizeof (EEmoticonToolButtonPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = emoticon_tool_button_set_property;
+       object_class->get_property = emoticon_tool_button_get_property;
+       object_class->dispose = emoticon_tool_button_dispose;
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->button_press_event = emoticon_tool_button_press_event;
+
+       toggle_tool_button_class = GTK_TOGGLE_TOOL_BUTTON_CLASS (class);
+       toggle_tool_button_class->toggled = emoticon_tool_button_toggled;
+
+       class->popup = emoticon_tool_button_popup;
+       class->popdown = emoticon_tool_button_popdown;
+
+       g_object_class_override_property (
+               object_class, PROP_CURRENT_EMOTICON, "current-emoticon");
+
+       g_object_class_install_property (
+               object_class,
+               PROP_POPUP_SHOWN,
+               g_param_spec_boolean (
+                       "popup-shown",
+                       "Popup Shown",
+                       "Whether the button's dropdown is shown",
+                       FALSE,
+                       G_PARAM_READWRITE));
+
+       signals[POPUP] = g_signal_new (
+               "popup",
+               G_OBJECT_CLASS_TYPE (class),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EEmoticonToolButtonClass, popup),
+               NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+
+       signals[POPDOWN] = g_signal_new (
+               "popdown",
+               G_OBJECT_CLASS_TYPE (class),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EEmoticonToolButtonClass, popdown),
+               NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+
+       gtk_binding_entry_add_signal (
+               gtk_binding_set_by_class (class),
+               GDK_KEY_Down, GDK_MOD1_MASK, "popup", 0);
+       gtk_binding_entry_add_signal (
+               gtk_binding_set_by_class (class),
+               GDK_KEY_KP_Down, GDK_MOD1_MASK, "popup", 0);
+
+       gtk_binding_entry_add_signal (
+               gtk_binding_set_by_class (class),
+               GDK_KEY_Up, GDK_MOD1_MASK, "popdown", 0);
+       gtk_binding_entry_add_signal (
+               gtk_binding_set_by_class (class),
+               GDK_KEY_KP_Up, GDK_MOD1_MASK, "popdown", 0);
+       gtk_binding_entry_add_signal (
+               gtk_binding_set_by_class (class),
+               GDK_KEY_Escape, 0, "popdown", 0);
+}
+
+static void
+e_emoticon_tool_button_interface_init (EEmoticonChooserInterface *interface)
+{
+       interface->get_current_emoticon =
+               emoticon_tool_button_get_current_emoticon;
+       interface->set_current_emoticon =
+               emoticon_tool_button_set_current_emoticon;
+}
+
+static void
+e_emoticon_tool_button_init (EEmoticonToolButton *button)
+{
+       EEmoticonChooser *chooser;
+       GtkWidget *toplevel;
+       GtkWidget *container;
+       GtkWidget *widget;
+       GtkWidget *window;
+       GList *list, *iter;
+       gint ii;
+
+       button->priv = E_EMOTICON_TOOL_BUTTON_GET_PRIVATE (button);
+
+       /* Build the pop-up window. */
+
+       window = gtk_window_new (GTK_WINDOW_POPUP);
+       gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
+       gtk_window_set_type_hint (
+               GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_COMBO);
+       button->priv->window = g_object_ref_sink (window);
+
+       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+       if (GTK_IS_WINDOW (toplevel)) {
+               gtk_window_group_add_window (
+                       gtk_window_get_group (GTK_WINDOW (toplevel)),
+                       GTK_WINDOW (window));
+               gtk_window_set_transient_for (
+                       GTK_WINDOW (window), GTK_WINDOW (toplevel));
+       }
+
+       g_signal_connect_swapped (
+               window, "show",
+               G_CALLBACK (emoticon_tool_button_child_show_cb), button);
+       g_signal_connect_swapped (
+               window, "hide",
+               G_CALLBACK (emoticon_tool_button_child_hide_cb), button);
+       g_signal_connect_swapped (
+               window, "button-release-event",
+               G_CALLBACK (emoticon_tool_button_button_release_event_cb),
+               button);
+       g_signal_connect_swapped (
+               window, "key-press-event",
+               G_CALLBACK (emoticon_tool_button_child_key_press_event_cb),
+               button);
+
+       /* Build the pop-up window contents. */
+
+       widget = gtk_frame_new (NULL);
+       gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_OUT);
+       gtk_container_add (GTK_CONTAINER (window), widget);
+       gtk_widget_show (widget);
+
+       container = widget;
+
+       widget = gtk_table_new (NUM_ROWS, NUM_COLS, TRUE);
+       gtk_table_set_row_spacings (GTK_TABLE (widget), 0);
+       gtk_table_set_col_spacings (GTK_TABLE (widget), 0);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       button->priv->table = g_object_ref (widget);
+       gtk_widget_show (widget);
+
+       container = widget;
+
+       chooser = E_EMOTICON_CHOOSER (button);
+       list = e_emoticon_chooser_get_items ();
+       g_assert (g_list_length (list) <= NUM_ROWS * NUM_COLS);
+
+       for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) {
+               EEmoticon *emoticon = iter->data;
+               guint left = ii % NUM_COLS;
+               guint top = ii / NUM_COLS;
+               gchar *tooltip;
+
+               tooltip = emoticon_tool_button_elide_underscores (
+                       gettext (emoticon->label));
+
+               widget = gtk_button_new ();
+               gtk_button_set_image (
+                       GTK_BUTTON (widget),
+                       gtk_image_new_from_icon_name (
+                       emoticon->icon_name, GTK_ICON_SIZE_BUTTON));
+               gtk_button_set_relief (GTK_BUTTON (widget), GTK_RELIEF_NONE);
+               gtk_widget_set_tooltip_text (widget, tooltip);
+               gtk_widget_show (widget);
+
+               g_object_set_data_full (
+                       G_OBJECT (widget), "emoticon",
+                       e_emoticon_copy (emoticon),
+                       (GDestroyNotify) e_emoticon_free);
+
+               g_signal_connect_swapped (
+                       widget, "clicked",
+                       G_CALLBACK (emoticon_tool_button_emoticon_clicked_cb),
+                       button);
+
+               g_signal_connect_swapped (
+                       widget, "clicked",
+                       G_CALLBACK (e_emoticon_chooser_item_activated),
+                       chooser);
+
+               g_signal_connect_swapped (
+                       widget, "button-release-event",
+                       G_CALLBACK (emoticon_tool_button_emoticon_release_event_cb),
+                       button);
+
+               gtk_table_attach (
+                       GTK_TABLE (container), widget,
+                       left, left + 1, top, top + 1, 0, 0, 0, 0);
+
+               g_free (tooltip);
+       }
+
+       g_list_free (list);
+}
+
+GtkToolItem *
+e_emoticon_tool_button_new (void)
+{
+       return g_object_new (E_TYPE_EMOTICON_TOOL_BUTTON, NULL);
+}
+
+void
+e_emoticon_tool_button_popup (EEmoticonToolButton *button)
+{
+       g_return_if_fail (E_IS_EMOTICON_TOOL_BUTTON (button));
+
+       g_signal_emit (button, signals[POPUP], 0);
+}
+
+void
+e_emoticon_tool_button_popdown (EEmoticonToolButton *button)
+{
+       g_return_if_fail (E_IS_EMOTICON_TOOL_BUTTON (button));
+
+       g_signal_emit (button, signals[POPDOWN], 0);
+}
diff --git a/e-util/e-emoticon-tool-button.h b/e-util/e-emoticon-tool-button.h
new file mode 100644
index 0000000..fc7dc8e
--- /dev/null
+++ b/e-util/e-emoticon-tool-button.h
@@ -0,0 +1,75 @@
+/*
+ * e-emoticon-tool-button.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_TOOL_BUTTON_H
+#define E_EMOTICON_TOOL_BUTTON_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_TOOL_BUTTON \
+       (e_emoticon_tool_button_get_type ())
+#define E_EMOTICON_TOOL_BUTTON(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButton))
+#define E_EMOTICON_TOOL_BUTTON_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButtonClass))
+#define E_IS_EMOTICON_TOOL_BUTTON(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_EMOTICON_TOOL_BUTTON))
+#define E_IS_EMOTICON_TOOL_BUTTON_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_EMOTICON_TOOL_BUTTON))
+#define E_EMOTICON_TOOL_BUTTON_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButtonClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonToolButton EEmoticonToolButton;
+typedef struct _EEmoticonToolButtonClass EEmoticonToolButtonClass;
+typedef struct _EEmoticonToolButtonPrivate EEmoticonToolButtonPrivate;
+
+struct _EEmoticonToolButton {
+       GtkToggleToolButton parent;
+       EEmoticonToolButtonPrivate *priv;
+};
+
+struct _EEmoticonToolButtonClass {
+       GtkToggleToolButtonClass parent_class;
+
+       void            (*popup)                (EEmoticonToolButton *button);
+       void            (*popdown)              (EEmoticonToolButton *button);
+};
+
+GType          e_emoticon_tool_button_get_type (void) G_GNUC_CONST;
+GtkToolItem *  e_emoticon_tool_button_new      (void);
+void           e_emoticon_tool_button_popup    (EEmoticonToolButton *button);
+void           e_emoticon_tool_button_popdown  (EEmoticonToolButton *button);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_TOOL_BUTTON_H */
diff --git a/e-util/e-emoticon.c b/e-util/e-emoticon.c
new file mode 100644
index 0000000..c543e52
--- /dev/null
+++ b/e-util/e-emoticon.c
@@ -0,0 +1,118 @@
+/*
+ * e-emoticon.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "e-emoticon.h"
+
+#include <gtk/gtk.h>
+
+static EEmoticon *
+emoticon_copy (EEmoticon *emoticon)
+{
+       EEmoticon *copy;
+
+       copy = g_slice_new (EEmoticon);
+       copy->label = g_strdup (emoticon->label);
+       copy->icon_name = g_strdup (emoticon->icon_name);
+       copy->text_face = g_strdup (emoticon->text_face);
+
+       return copy;
+}
+
+static void
+emoticon_free (EEmoticon *emoticon)
+{
+       g_free (emoticon->label);
+       g_free (emoticon->icon_name);
+       g_free (emoticon->text_face);
+       g_slice_free (EEmoticon, emoticon);
+}
+
+GType
+e_emoticon_get_type (void)
+{
+       static GType type = 0;
+
+       if (G_UNLIKELY (type == 0))
+               type = g_boxed_type_register_static (
+                       "EEmoticon",
+                       (GBoxedCopyFunc) emoticon_copy,
+                       (GBoxedFreeFunc) emoticon_free);
+
+       return type;
+}
+
+gboolean
+e_emoticon_equal (EEmoticon *emoticon_a,
+                  EEmoticon *emoticon_b)
+{
+       if (((emoticon_a == NULL) && (emoticon_b != NULL)) ||
+           ((emoticon_a != NULL) && (emoticon_b == NULL)))
+               return FALSE;
+
+       if (emoticon_a == emoticon_b)
+               return TRUE;
+
+       if (g_strcmp0 (emoticon_a->label, emoticon_b->label) != 0)
+               return FALSE;
+
+       if (g_strcmp0 (emoticon_a->icon_name, emoticon_b->icon_name) != 0)
+               return FALSE;
+
+       if (g_strcmp0 (emoticon_a->text_face, emoticon_b->text_face) != 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+EEmoticon *
+e_emoticon_copy (EEmoticon *emoticon)
+{
+       return g_boxed_copy (E_TYPE_EMOTICON, emoticon);
+}
+
+void
+e_emoticon_free (EEmoticon *emoticon)
+{
+       g_boxed_free (E_TYPE_EMOTICON, emoticon);
+}
+
+gchar *
+e_emoticon_get_uri (EEmoticon *emoticon)
+{
+       GtkIconInfo *icon_info;
+       GtkIconTheme *icon_theme;
+       const gchar *filename;
+       gchar *uri = NULL;
+
+       icon_theme = gtk_icon_theme_get_default ();
+       icon_info = gtk_icon_theme_lookup_icon (
+               icon_theme, emoticon->icon_name, 16, 0);
+       g_return_val_if_fail (icon_info != NULL, NULL);
+
+       filename = gtk_icon_info_get_filename (icon_info);
+       if (filename != NULL) {
+               uri = g_filename_to_uri (filename, NULL, NULL);
+       }
+       gtk_icon_info_free (icon_info);
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       return uri;
+}
diff --git a/e-util/e-emoticon.h b/e-util/e-emoticon.h
new file mode 100644
index 0000000..66327ab
--- /dev/null
+++ b/e-util/e-emoticon.h
@@ -0,0 +1,53 @@
+/*
+ * e-emoticon.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_H
+#define E_EMOTICON_H
+
+#include <glib-object.h>
+
+#define E_TYPE_EMOTICON \
+       (e_emoticon_get_type ())
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticon EEmoticon;
+
+struct _EEmoticon {
+       gchar *label;
+       gchar *icon_name;
+       gchar *text_face;
+};
+
+GType          e_emoticon_get_type             (void) G_GNUC_CONST;
+gboolean       e_emoticon_equal                (EEmoticon *emoticon_a,
+                                                EEmoticon *emoticon_b);
+EEmoticon *    e_emoticon_copy                 (EEmoticon *emoticon);
+void           e_emoticon_free                 (EEmoticon *emoticon);
+gchar *                e_emoticon_get_uri              (EEmoticon *emoticon);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_H */
diff --git a/e-util/e-focus-tracker.c b/e-util/e-focus-tracker.c
index 0879a16..3d3daec 100644
--- a/e-util/e-focus-tracker.c
+++ b/e-util/e-focus-tracker.c
@@ -28,6 +28,7 @@
 
 #include "e-selectable.h"
 #include "e-widget-undo.h"
+#include "e-html-editor-view.h"
 
 #define E_FOCUS_TRACKER_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
@@ -255,6 +256,42 @@ focus_tracker_text_view_update_actions (EFocusTracker *focus_tracker,
 }
 
 static void
+focus_tracker_editor_update_actions (EFocusTracker *focus_tracker,
+                                     EHTMLEditorView *view,
+                                     GdkAtom *targets,
+                                     gint n_targets)
+{
+       GtkAction *action;
+       gboolean can_copy;
+       gboolean can_cut;
+       gboolean can_paste;
+
+       g_object_get (view,
+                     "can-copy", &can_copy,
+                     "can-cut", &can_cut,
+                     "can-paste", &can_paste,
+                     NULL);
+
+       action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
+       if (action != NULL) {
+               gtk_action_set_sensitive (action, can_cut);
+               gtk_action_set_tooltip (action, _("Cut the selection"));
+       }
+
+       action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
+       if (action != NULL) {
+               gtk_action_set_sensitive (action, can_copy);
+               gtk_action_set_tooltip (action, _("Copy the selection"));
+       }
+
+       action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
+       if (action != NULL) {
+               gtk_action_set_sensitive (action, can_paste);
+               gtk_action_set_tooltip (action, _("Paste the clipboard"));
+       }
+}
+
+static void
 focus_tracker_selectable_update_actions (EFocusTracker *focus_tracker,
                                          ESelectable *selectable,
                                          GdkAtom *targets,
@@ -331,6 +368,11 @@ focus_tracker_targets_received_cb (GtkClipboard *clipboard,
                        focus_tracker, GTK_TEXT_VIEW (focus),
                        targets, n_targets);
 
+       else if (E_IS_HTML_EDITOR_VIEW (focus))
+               focus_tracker_editor_update_actions (
+                       focus_tracker, E_HTML_EDITOR_VIEW (focus),
+                       targets, n_targets);
+
        g_object_unref (focus_tracker);
 }
 
@@ -349,6 +391,9 @@ focus_tracker_set_focus_cb (GtkWindow *window,
                if (GTK_IS_TEXT_VIEW (focus))
                        break;
 
+               if (E_IS_HTML_EDITOR_VIEW (focus))
+                       break;
+
                focus = gtk_widget_get_parent (focus);
        }
 
diff --git a/e-util/e-html-editor-actions.c b/e-util/e-html-editor-actions.c
new file mode 100644
index 0000000..6db3ad4
--- /dev/null
+++ b/e-util/e-html-editor-actions.c
@@ -0,0 +1,2028 @@
+/* e-html-editor-actions.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <enchant/enchant.h>
+
+#include "e-html-editor.h"
+#include "e-html-editor-private.h"
+#include "e-html-editor-actions.h"
+#include "e-html-editor-utils.h"
+#include "e-emoticon-action.h"
+#include "e-emoticon-chooser.h"
+#include "e-image-chooser-dialog.h"
+#include "e-spell-checker.h"
+
+static void
+insert_html_file_ready_cb (GFile *file,
+                           GAsyncResult *result,
+                           EHTMLEditor *editor)
+{
+       EHTMLEditorSelection *selection;
+       gchar *contents = NULL;
+       gsize length;
+       GError *error = NULL;
+
+       g_file_load_contents_finish (
+               file, result, &contents, &length, NULL, &error);
+       if (error != NULL) {
+               GtkWidget *dialog;
+
+               dialog = gtk_message_dialog_new (
+                       GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))),
+                       0, GTK_MESSAGE_ERROR,
+                       GTK_BUTTONS_CLOSE, _("Failed to insert HTML file."));
+               gtk_message_dialog_format_secondary_text (
+                       GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
+               gtk_dialog_run (GTK_DIALOG (dialog));
+               gtk_widget_destroy (dialog);
+
+               g_clear_error (&error);
+               g_object_unref (editor);
+               return;
+       }
+
+       selection = e_html_editor_view_get_selection (
+               e_html_editor_get_view (editor));
+       e_html_editor_selection_insert_html (selection, contents);
+       g_free (contents);
+
+       g_object_unref (editor);
+}
+
+static void
+insert_text_file_ready_cb (GFile *file,
+                           GAsyncResult *result,
+                           EHTMLEditor *editor)
+{
+       EHTMLEditorSelection *selection;
+       gchar *contents;
+       gsize length;
+       GError *error = NULL;
+
+       g_file_load_contents_finish (
+               file, result, &contents, &length, NULL, &error);
+       if (error != NULL) {
+               GtkWidget *dialog;
+
+               dialog = gtk_message_dialog_new (
+                       GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))),
+                       0, GTK_MESSAGE_ERROR,
+                       GTK_BUTTONS_CLOSE, _("Failed to insert text file."));
+               gtk_message_dialog_format_secondary_text (
+                       GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
+               gtk_dialog_run (GTK_DIALOG (dialog));
+               gtk_widget_destroy (dialog);
+
+               g_clear_error (&error);
+               g_object_unref (editor);
+               return;
+       }
+
+       selection = e_html_editor_view_get_selection (
+               e_html_editor_get_view (editor));
+       e_html_editor_selection_insert_text (selection, contents);
+       g_free (contents);
+
+       g_object_unref (editor);
+}
+
+static void
+editor_update_static_spell_actions (EHTMLEditor *editor)
+{
+       ESpellChecker *checker;
+       EHTMLEditorView *view;
+       guint count;
+
+       view = e_html_editor_get_view (editor);
+       checker = e_html_editor_view_get_spell_checker (view);
+
+       count = e_spell_checker_count_active_languages (checker);
+
+       gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD), count == 1);
+       gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD_MENU), count > 1);
+       gtk_action_set_visible (ACTION (CONTEXT_SPELL_IGNORE), count > 0);
+
+       gtk_action_set_sensitive (ACTION (SPELL_CHECK), count > 0);
+}
+
+/*****************************************************************************
+ * Action Callbacks
+ *****************************************************************************/
+
+static void
+action_context_delete_cell_cb (GtkAction *action,
+                               EHTMLEditor *editor)
+{
+       WebKitDOMNode *sibling;
+       WebKitDOMElement *cell;
+
+       g_return_if_fail (editor->priv->table_cell != NULL);
+
+       cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
+       if (!cell) {
+               cell = e_html_editor_dom_node_find_parent_element (
+                                       editor->priv->table_cell, "TH");
+       }
+       g_return_if_fail (cell != NULL);
+
+       sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (cell));
+       if (!sibling) {
+               sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (cell));
+       }
+
+       webkit_dom_node_remove_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (cell)),
+               WEBKIT_DOM_NODE (cell), NULL);
+
+       if (sibling) {
+               webkit_dom_html_table_cell_element_set_col_span (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (sibling),
+                       webkit_dom_html_table_cell_element_get_col_span (
+                               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (sibling)) + 1);
+       }
+}
+
+static void
+action_context_delete_column_cb (GtkAction *action,
+                                 EHTMLEditor *editor)
+{
+       WebKitDOMElement *cell, *table;
+       WebKitDOMHTMLCollection *rows;
+       gulong index, length, ii;
+
+       g_return_if_fail (editor->priv->table_cell != NULL);
+
+       /* Find TD in which the selection starts */
+       cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
+       if (!cell) {
+               cell = e_html_editor_dom_node_find_parent_element (
+                                       editor->priv->table_cell, "TH");
+       }
+       g_return_if_fail (cell != NULL);
+
+       table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       rows = webkit_dom_html_table_element_get_rows (
+                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
+       length = webkit_dom_html_collection_get_length (rows);
+
+       index = webkit_dom_html_table_cell_element_get_cell_index (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *row;
+
+               row = webkit_dom_html_collection_item (rows, ii);
+
+               webkit_dom_html_table_row_element_delete_cell (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index, NULL);
+       }
+}
+
+static void
+action_context_delete_row_cb (GtkAction *action,
+                              EHTMLEditor *editor)
+{
+       WebKitDOMElement *row;
+
+       g_return_if_fail (editor->priv->table_cell != NULL);
+
+       row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR");
+       g_return_if_fail (row != NULL);
+
+       webkit_dom_node_remove_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)),
+               WEBKIT_DOM_NODE (row), NULL);
+}
+
+static void
+action_context_delete_table_cb (GtkAction *action,
+                                EHTMLEditor *editor)
+{
+       WebKitDOMElement *table;
+
+       g_return_if_fail (editor->priv->table_cell != NULL);
+
+       table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE");
+       g_return_if_fail (table != NULL);
+
+       webkit_dom_node_remove_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)),
+               WEBKIT_DOM_NODE (table), NULL);
+}
+
+static void
+action_context_insert_column_after_cb (GtkAction *action,
+                                       EHTMLEditor *editor)
+{
+       WebKitDOMElement *cell, *row;
+       gulong index;
+
+       g_return_if_fail (editor->priv->table_cell != NULL);
+
+       cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
+       if (!cell) {
+               cell = e_html_editor_dom_node_find_parent_element (
+                                       editor->priv->table_cell, "TH");
+       }
+       g_return_if_fail (cell != NULL);
+
+       row = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR");
+       g_return_if_fail (row != NULL);
+
+       /* Get the first row in the table */
+       row = WEBKIT_DOM_ELEMENT (
+               webkit_dom_node_get_first_child (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row))));
+
+       index = webkit_dom_html_table_cell_element_get_cell_index (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+
+       while (row) {
+               webkit_dom_html_table_row_element_insert_cell (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index + 1, NULL);
+
+               row = WEBKIT_DOM_ELEMENT (
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
+       }
+}
+
+static void
+action_context_insert_column_before_cb (GtkAction *action,
+                                        EHTMLEditor *editor)
+{
+       WebKitDOMElement *cell, *row;
+       gulong index;
+
+       g_return_if_fail (editor->priv->table_cell != NULL);
+
+       cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
+       if (!cell) {
+               cell = e_html_editor_dom_node_find_parent_element (
+                               editor->priv->table_cell, "TH");
+       }
+       g_return_if_fail (cell != NULL);
+
+       row = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR");
+       g_return_if_fail (row != NULL);
+
+       /* Get the first row in the table */
+       row = WEBKIT_DOM_ELEMENT (
+               webkit_dom_node_get_first_child (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row))));
+
+       index = webkit_dom_html_table_cell_element_get_cell_index (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+
+       while (row) {
+               webkit_dom_html_table_row_element_insert_cell (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index - 1, NULL);
+
+               row = WEBKIT_DOM_ELEMENT (
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
+       }
+}
+
+static void
+action_context_insert_row_above_cb (GtkAction *action,
+                                    EHTMLEditor *editor)
+{
+       WebKitDOMElement *row, *table;
+       WebKitDOMHTMLCollection *cells;
+       WebKitDOMHTMLElement *new_row;
+       gulong index, cell_count, ii;
+
+       g_return_if_fail (editor->priv->table_cell != NULL);
+
+       row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR");
+       g_return_if_fail (row != NULL);
+
+       table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       index = webkit_dom_html_table_row_element_get_row_index (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+
+       new_row = webkit_dom_html_table_element_insert_row (
+                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index, NULL);
+
+       cells = webkit_dom_html_table_row_element_get_cells (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+       cell_count = webkit_dom_html_collection_get_length (cells);
+       for (ii = 0; ii < cell_count; ii++) {
+               webkit_dom_html_table_row_element_insert_cell (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL);
+       }
+
+}
+
+static void
+action_context_insert_row_below_cb (GtkAction *action,
+                                    EHTMLEditor *editor)
+{
+       WebKitDOMElement *row, *table;
+       WebKitDOMHTMLCollection *cells;
+       WebKitDOMHTMLElement *new_row;
+       gulong index, cell_count, ii;
+
+       g_return_if_fail (editor->priv->table_cell != NULL);
+
+       row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR");
+       g_return_if_fail (row != NULL);
+
+       table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       index = webkit_dom_html_table_row_element_get_row_index (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+
+       new_row = webkit_dom_html_table_element_insert_row (
+                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index + 1, NULL);
+
+       cells = webkit_dom_html_table_row_element_get_cells (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+       cell_count = webkit_dom_html_collection_get_length (cells);
+       for (ii = 0; ii < cell_count; ii++) {
+               webkit_dom_html_table_row_element_insert_cell (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL);
+       }
+}
+
+static void
+action_context_remove_link_cb (GtkAction *action,
+                               EHTMLEditor *editor)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       e_html_editor_selection_unlink (selection);
+}
+
+static void
+action_context_spell_add_cb (GtkAction *action,
+                             EHTMLEditor *editor)
+{
+       ESpellChecker *spell_checker;
+       EHTMLEditorSelection *selection;
+       gchar *word;
+
+       spell_checker = e_html_editor_view_get_spell_checker (
+               editor->priv->html_editor_view);
+       selection = e_html_editor_view_get_selection (editor->priv->html_editor_view);
+
+       word = e_html_editor_selection_get_caret_word (selection);
+       if (word && *word) {
+               e_spell_checker_learn_word (spell_checker, word);
+       }
+}
+
+static void
+action_context_spell_ignore_cb (GtkAction *action,
+                                EHTMLEditor *editor)
+{
+       ESpellChecker *spell_checker;
+       EHTMLEditorSelection *selection;
+       gchar *word;
+
+       spell_checker = e_html_editor_view_get_spell_checker (
+               editor->priv->html_editor_view);
+       selection = e_html_editor_view_get_selection (editor->priv->html_editor_view);
+
+       word = e_html_editor_selection_get_caret_word (selection);
+       if (word && *word) {
+               e_spell_checker_ignore_word (spell_checker, word);
+       }
+}
+
+static void
+action_copy_cb (GtkAction *action,
+                EHTMLEditor *editor)
+{
+       webkit_web_view_copy_clipboard (
+               WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+}
+
+static void
+action_cut_cb (GtkAction *action,
+               EHTMLEditor *editor)
+{
+       webkit_web_view_cut_clipboard (
+               WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+}
+
+static void
+action_indent_cb (GtkAction *action,
+                  EHTMLEditor *editor)
+{
+       e_html_editor_selection_indent (editor->priv->selection);
+}
+
+static void
+action_insert_emoticon_cb (GtkAction *action,
+                           EHTMLEditor *editor)
+{
+       EHTMLEditorView *view;
+       EEmoticon *emoticon;
+
+       emoticon = e_emoticon_chooser_get_current_emoticon (
+                                       E_EMOTICON_CHOOSER (action));
+       g_return_if_fail (emoticon != NULL);
+
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_insert_smiley (view, emoticon);
+}
+
+static void
+action_insert_html_file_cb (GtkToggleAction *action,
+                            EHTMLEditor *editor)
+{
+       GtkWidget *dialog;
+       GtkFileFilter *filter;
+
+       dialog = gtk_file_chooser_dialog_new (
+               _("Insert HTML File"), NULL,
+               GTK_FILE_CHOOSER_ACTION_OPEN,
+               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+               GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+
+       filter = gtk_file_filter_new ();
+       gtk_file_filter_set_name (filter, _("HTML file"));
+       gtk_file_filter_add_mime_type (filter, "text/html");
+       gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+               GFile *file;
+
+               file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+
+               /* XXX Need a way to cancel this. */
+               g_file_load_contents_async (
+                       file, NULL, (GAsyncReadyCallback)
+                       insert_html_file_ready_cb,
+                       g_object_ref (editor));
+
+               g_object_unref (file);
+       }
+
+       gtk_widget_destroy (dialog);
+}
+
+static void
+action_insert_image_cb (GtkAction *action,
+                        EHTMLEditor *editor)
+{
+       GtkWidget *dialog;
+
+       dialog = e_image_chooser_dialog_new (_("Insert Image"), NULL);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+               EHTMLEditorView *view;
+               EHTMLEditorSelection *selection;
+               gchar *uri;
+
+               uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
+
+               view = e_html_editor_get_view (editor);
+               selection = e_html_editor_view_get_selection (view);
+               e_html_editor_selection_insert_image (selection, uri);
+
+               g_free (uri);
+       }
+
+       gtk_widget_destroy (dialog);
+}
+
+static void
+action_insert_link_cb (GtkAction *action,
+                       EHTMLEditor *editor)
+{
+       if (editor->priv->link_dialog == NULL)
+               editor->priv->link_dialog =
+                       e_html_editor_link_dialog_new (editor);
+
+       gtk_window_present (GTK_WINDOW (editor->priv->link_dialog));
+}
+
+static void
+action_insert_rule_cb (GtkAction *action,
+                       EHTMLEditor *editor)
+{
+       if (editor->priv->hrule_dialog == NULL)
+               editor->priv->hrule_dialog =
+                       e_html_editor_hrule_dialog_new (editor);
+
+       gtk_window_present (GTK_WINDOW (editor->priv->hrule_dialog));
+}
+
+static void
+action_insert_table_cb (GtkAction *action,
+                        EHTMLEditor *editor)
+{
+       if (editor->priv->table_dialog == NULL)
+               editor->priv->table_dialog =
+                       e_html_editor_table_dialog_new (editor);
+
+       gtk_window_present (GTK_WINDOW (editor->priv->table_dialog));
+}
+
+static void
+action_insert_text_file_cb (GtkAction *action,
+                            EHTMLEditor *editor)
+{
+       GtkWidget *dialog;
+       GtkFileFilter *filter;
+
+       dialog = gtk_file_chooser_dialog_new (
+               _("Insert text file"), NULL,
+               GTK_FILE_CHOOSER_ACTION_OPEN,
+               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+               GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+
+       filter = gtk_file_filter_new ();
+       gtk_file_filter_set_name (filter, _("Text file"));
+       gtk_file_filter_add_mime_type (filter, "text/plain");
+       gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+               GFile *file;
+
+               file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+
+               /* XXX Need a way to cancel this. */
+               g_file_load_contents_async (
+                       file, NULL, (GAsyncReadyCallback)
+                       insert_text_file_ready_cb,
+                       g_object_ref (editor));
+
+               g_object_unref (file);
+       }
+
+       gtk_widget_destroy (dialog);
+}
+
+static void
+action_language_cb (GtkToggleAction *toggle_action,
+                    EHTMLEditor *editor)
+{
+       ESpellChecker *checker;
+       EHTMLEditorView *view;
+       const gchar *language_code;
+       GtkAction *add_action;
+       gchar *action_name;
+       gboolean active;
+
+       view = e_html_editor_get_view (editor);
+       checker = e_html_editor_view_get_spell_checker (view);
+       language_code = gtk_action_get_name (GTK_ACTION (toggle_action));
+
+       active = gtk_toggle_action_get_active (toggle_action);
+       e_spell_checker_set_language_active (checker, language_code, active);
+
+       /* Update "Add Word To" context menu item visibility. */
+       action_name = g_strdup_printf ("context-spell-add-%s", language_code);
+       add_action = e_html_editor_get_action (editor, action_name);
+       gtk_action_set_visible (add_action, active);
+       g_free (action_name);
+
+       editor_update_static_spell_actions (editor);
+
+       g_signal_emit_by_name (editor, "spell-languages-changed");
+}
+
+static gboolean
+update_mode_combobox (gpointer data)
+{
+       EHTMLEditor *editor = data;
+       EHTMLEditorView *view;
+       GtkAction *action;
+       gboolean is_html;
+
+       if (!E_IS_HTML_EDITOR (editor))
+               return FALSE;
+
+       view = e_html_editor_get_view (editor);
+       is_html = e_html_editor_view_get_html_mode (view);
+
+       action = gtk_action_group_get_action (
+               editor->priv->core_actions, "mode-html");
+       gtk_radio_action_set_current_value (
+               GTK_RADIO_ACTION (action), (is_html ? 1 : 0));
+
+       return FALSE;
+}
+
+static void
+action_mode_cb (GtkRadioAction *action,
+                GtkRadioAction *current,
+                EHTMLEditor *editor)
+{
+       GtkActionGroup *action_group;
+       EHTMLEditorView *view;
+       GtkWidget *style_combo_box;
+       gboolean is_html;
+
+       view = e_html_editor_get_view (editor);
+       is_html = e_html_editor_view_get_html_mode (view);
+
+       /* This must be done from idle callback, because apparently we can change
+        * current value in callback of current value change */
+       g_idle_add (update_mode_combobox, editor);
+
+       action_group = editor->priv->html_actions;
+       gtk_action_group_set_sensitive (action_group, is_html);
+
+       action_group = editor->priv->html_context_actions;
+       gtk_action_group_set_visible (action_group, is_html);
+
+       gtk_widget_set_sensitive (editor->priv->color_combo_box, is_html);
+
+       if (is_html) {
+               gtk_widget_show (editor->priv->html_toolbar);
+       } else {
+               gtk_widget_hide (editor->priv->html_toolbar);
+       }
+
+       /* Certain paragraph styles are HTML-only. */
+       gtk_action_set_sensitive (ACTION (STYLE_H1), is_html);
+       gtk_action_set_visible (ACTION (STYLE_H1), is_html);
+       gtk_action_set_sensitive (ACTION (STYLE_H2), is_html);
+       gtk_action_set_visible (ACTION (STYLE_H2), is_html);
+       gtk_action_set_sensitive (ACTION (STYLE_H3), is_html);
+       gtk_action_set_visible (ACTION (STYLE_H3), is_html);
+       gtk_action_set_sensitive (ACTION (STYLE_H4), is_html);
+       gtk_action_set_visible (ACTION (STYLE_H4), is_html);
+       gtk_action_set_sensitive (ACTION (STYLE_H5), is_html);
+       gtk_action_set_visible (ACTION (STYLE_H5), is_html);
+       gtk_action_set_sensitive (ACTION (STYLE_H6), is_html);
+       gtk_action_set_visible (ACTION (STYLE_H6), is_html);
+       gtk_action_set_sensitive (ACTION (STYLE_ADDRESS), is_html);
+       gtk_action_set_visible (ACTION (STYLE_ADDRESS), is_html);
+
+       /* Hide them from the action combo box as well */
+       style_combo_box = e_html_editor_get_style_combo_box (editor);
+       e_action_combo_box_update_model (E_ACTION_COMBO_BOX (style_combo_box));
+}
+
+static void
+action_paste_cb (GtkAction *action,
+                 EHTMLEditor *editor)
+{
+       EHTMLEditorView *view = e_html_editor_get_view (editor);
+
+       /* Paste only if WebView has focus */
+       if (gtk_widget_has_focus (GTK_WIDGET (view))) {
+               webkit_web_view_paste_clipboard (
+                       WEBKIT_WEB_VIEW (view));
+
+               e_html_editor_view_force_spell_check (view);
+       }
+}
+
+static void
+action_paste_quote_cb (GtkAction *action,
+                       EHTMLEditor *editor)
+{
+       e_html_editor_view_paste_clipboard_quoted (
+               e_html_editor_get_view (editor));
+
+       e_html_editor_view_force_spell_check (
+               e_html_editor_get_view (editor));
+}
+
+static void
+action_properties_cell_cb (GtkAction *action,
+                           EHTMLEditor *editor)
+{
+       if (editor->priv->cell_dialog == NULL) {
+               editor->priv->cell_dialog =
+                       e_html_editor_cell_dialog_new (editor);
+       }
+
+       e_html_editor_cell_dialog_show (
+               E_HTML_EDITOR_CELL_DIALOG (editor->priv->cell_dialog),
+               editor->priv->table_cell);
+}
+
+static void
+action_properties_image_cb (GtkAction *action,
+                            EHTMLEditor *editor)
+{
+       if (editor->priv->image_dialog == NULL) {
+               editor->priv->image_dialog =
+                       e_html_editor_image_dialog_new (editor);
+       }
+
+       e_html_editor_image_dialog_show (
+               E_HTML_EDITOR_IMAGE_DIALOG (editor->priv->image_dialog),
+               editor->priv->image);
+}
+
+static void
+action_properties_link_cb (GtkAction *action,
+                           EHTMLEditor *editor)
+{
+       if (editor->priv->link_dialog == NULL) {
+               editor->priv->link_dialog =
+                       e_html_editor_link_dialog_new (editor);
+       }
+
+       gtk_window_present (GTK_WINDOW (editor->priv->link_dialog));
+}
+
+static void
+action_properties_page_cb (GtkAction *action,
+                           EHTMLEditor *editor)
+{
+       if (editor->priv->page_dialog == NULL) {
+               editor->priv->page_dialog =
+                       e_html_editor_page_dialog_new (editor);
+       }
+
+       gtk_window_present (GTK_WINDOW (editor->priv->page_dialog));
+}
+
+static void
+action_properties_paragraph_cb (GtkAction *action,
+                                EHTMLEditor *editor)
+{
+       if (editor->priv->paragraph_dialog == NULL) {
+               editor->priv->paragraph_dialog =
+                       e_html_editor_paragraph_dialog_new (editor);
+       }
+
+       gtk_window_present (GTK_WINDOW (editor->priv->paragraph_dialog));
+}
+
+static void
+action_properties_rule_cb (GtkAction *action,
+                           EHTMLEditor *editor)
+{
+       if (editor->priv->hrule_dialog == NULL) {
+               editor->priv->hrule_dialog =
+                       e_html_editor_hrule_dialog_new (editor);
+       }
+
+       gtk_window_present (GTK_WINDOW (editor->priv->hrule_dialog));
+}
+
+static void
+action_properties_table_cb (GtkAction *action,
+                            EHTMLEditor *editor)
+{
+       if (editor->priv->table_dialog == NULL) {
+               editor->priv->table_dialog =
+                       e_html_editor_table_dialog_new (editor);
+       }
+
+       gtk_window_present (GTK_WINDOW (editor->priv->table_dialog));
+}
+
+static void
+action_properties_text_cb (GtkAction *action,
+                           EHTMLEditor *editor)
+{
+       if (editor->priv->text_dialog == NULL) {
+               editor->priv->text_dialog =
+                       e_html_editor_text_dialog_new (editor);
+       }
+
+       gtk_window_present (GTK_WINDOW (editor->priv->text_dialog));
+}
+
+static void
+action_redo_cb (GtkAction *action,
+                EHTMLEditor *editor)
+{
+       webkit_web_view_redo (
+               WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+}
+
+static void
+action_select_all_cb (GtkAction *action,
+                      EHTMLEditor *editor)
+{
+       webkit_web_view_select_all (
+               WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+}
+
+static void
+action_show_find_cb (GtkAction *action,
+                     EHTMLEditor *editor)
+{
+       if (editor->priv->find_dialog == NULL) {
+               editor->priv->find_dialog = e_html_editor_find_dialog_new (editor);
+               gtk_action_set_sensitive (ACTION (FIND_AGAIN), TRUE);
+       }
+
+       gtk_window_present (GTK_WINDOW (editor->priv->find_dialog));
+}
+
+static void
+action_find_again_cb (GtkAction *action,
+                      EHTMLEditor *editor)
+{
+       if (editor->priv->find_dialog == NULL) {
+               return;
+       }
+
+       e_html_editor_find_dialog_find_next (
+               E_HTML_EDITOR_FIND_DIALOG (editor->priv->find_dialog));
+}
+
+static void
+action_show_replace_cb (GtkAction *action,
+                        EHTMLEditor *editor)
+{
+       if (editor->priv->replace_dialog == NULL) {
+               editor->priv->replace_dialog =
+                       e_html_editor_replace_dialog_new (editor);
+       }
+
+       gtk_window_present (GTK_WINDOW (editor->priv->replace_dialog));
+}
+
+static void
+action_spell_check_cb (GtkAction *action,
+                       EHTMLEditor *editor)
+{
+       if (editor->priv->spell_check_dialog == NULL) {
+               editor->priv->spell_check_dialog =
+                       e_html_editor_spell_check_dialog_new (editor);
+       }
+
+       gtk_window_present (GTK_WINDOW (editor->priv->spell_check_dialog));
+}
+
+static void
+action_undo_cb (GtkAction *action,
+                EHTMLEditor *editor)
+{
+       webkit_web_view_undo (
+               WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+}
+
+static void
+action_unindent_cb (GtkAction *action,
+                    EHTMLEditor *editor)
+{
+       e_html_editor_selection_unindent (editor->priv->selection);
+}
+
+static void
+action_wrap_lines_cb (GtkAction *action,
+                      EHTMLEditor *editor)
+{
+       e_html_editor_selection_wrap_lines (editor->priv->selection);
+}
+
+static void
+action_show_webkit_inspector_cb (GtkAction *action,
+                                 EHTMLEditor *editor)
+{
+       WebKitWebInspector *inspector;
+       EHTMLEditorView *view;
+
+       view = e_html_editor_get_view (editor);
+       inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (view));
+
+       webkit_web_inspector_show (inspector);
+}
+
+/*****************************************************************************
+ * Core Actions
+ *
+ * These actions are always enabled.
+ *****************************************************************************/
+
+static GtkActionEntry core_entries[] = {
+
+       { "copy",
+         GTK_STOCK_COPY,
+         N_("_Copy"),
+         "<Control>c",
+         N_("Copy selected text to the clipboard"),
+         G_CALLBACK (action_copy_cb) },
+
+       { "cut",
+         GTK_STOCK_CUT,
+         N_("Cu_t"),
+         "<Control>x",
+         N_("Cut selected text to the clipboard"),
+         G_CALLBACK (action_cut_cb) },
+
+       { "indent",
+         GTK_STOCK_INDENT,
+         N_("_Increase Indent"),
+         "<Control>bracketright",
+         N_("Increase Indent"),
+         G_CALLBACK (action_indent_cb) },
+
+       { "insert-html-file",
+         NULL,
+         N_("_HTML File..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_insert_html_file_cb) },
+
+       { "insert-text-file",
+         NULL,
+         N_("Te_xt File..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_insert_text_file_cb) },
+
+       { "paste",
+         GTK_STOCK_PASTE,
+         N_("_Paste"),
+         "<Control>v",
+         N_("Paste text from the clipboard"),
+         G_CALLBACK (action_paste_cb) },
+
+       { "paste-quote",
+         NULL,
+         N_("Paste _Quotation"),
+         "<Shift><Control>v",
+         NULL,
+         G_CALLBACK (action_paste_quote_cb) },
+
+       { "redo",
+         GTK_STOCK_REDO,
+         N_("_Redo"),
+         "<Shift><Control>z",
+         N_("Redo the last undone action"),
+         G_CALLBACK (action_redo_cb) },
+
+       { "select-all",
+         GTK_STOCK_SELECT_ALL,
+         N_("Select _All"),
+         "<Control>a",
+         NULL,
+         G_CALLBACK (action_select_all_cb) },
+
+       { "show-find",
+         GTK_STOCK_FIND,
+         N_("_Find..."),
+         "<Control>f",
+         N_("Search for text"),
+         G_CALLBACK (action_show_find_cb) },
+
+       { "find-again",
+         NULL,
+         N_("Find A_gain"),
+         "<Control>g",
+         NULL,
+         G_CALLBACK (action_find_again_cb) },
+
+       { "show-replace",
+         GTK_STOCK_FIND_AND_REPLACE,
+         N_("Re_place..."),
+         "<Control>h",
+         N_("Search for and replace text"),
+         G_CALLBACK (action_show_replace_cb) },
+
+       { "spell-check",
+         GTK_STOCK_SPELL_CHECK,
+         N_("Check _Spelling..."),
+         "F7",
+         NULL,
+         G_CALLBACK (action_spell_check_cb) },
+
+       { "undo",
+         GTK_STOCK_UNDO,
+         N_("_Undo"),
+         "<Control>z",
+         N_("Undo the last action"),
+         G_CALLBACK (action_undo_cb) },
+
+       { "unindent",
+         GTK_STOCK_UNINDENT,
+         N_("_Decrease Indent"),
+         "<Control>bracketleft",
+         N_("Decrease Indent"),
+         G_CALLBACK (action_unindent_cb) },
+
+       { "wrap-lines",
+         NULL,
+         N_("_Wrap Lines"),
+         "<Control>k",
+         NULL,
+         G_CALLBACK (action_wrap_lines_cb) },
+
+       { "webkit-inspector",
+          NULL,
+          N_("Open Inspector"),
+          NULL,
+          NULL,
+          G_CALLBACK (action_show_webkit_inspector_cb) },
+
+       /* Menus */
+
+       { "edit-menu",
+         NULL,
+         N_("_Edit"),
+         NULL,
+         NULL,
+         NULL },
+
+       { "file-menu",
+         NULL,
+         N_("_File"),
+         NULL,
+         NULL,
+         NULL },
+
+       { "format-menu",
+         NULL,
+         N_("For_mat"),
+         NULL,
+         NULL,
+         NULL },
+
+       { "paragraph-style-menu",
+         NULL,
+         N_("_Paragraph Style"),
+         NULL,
+         NULL,
+         NULL },
+
+       { "insert-menu",
+         NULL,
+         N_("_Insert"),
+         NULL,
+         NULL,
+         NULL },
+
+       { "justify-menu",
+         NULL,
+         N_("_Alignment"),
+         NULL,
+         NULL,
+         NULL },
+
+       { "language-menu",
+         NULL,
+         N_("Current _Languages"),
+         NULL,
+         NULL,
+         NULL },
+
+       { "view-menu",
+         NULL,
+         N_("_View"),
+         NULL,
+         NULL,
+         NULL }
+};
+
+static GtkRadioActionEntry core_justify_entries[] = {
+
+       { "justify-center",
+         GTK_STOCK_JUSTIFY_CENTER,
+         N_("_Center"),
+         "<Control>e",
+         N_("Center Alignment"),
+         E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER },
+
+       { "justify-left",
+         GTK_STOCK_JUSTIFY_LEFT,
+         N_("_Left"),
+         "<Control>l",
+         N_("Left Alignment"),
+         E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT },
+
+       { "justify-right",
+         GTK_STOCK_JUSTIFY_RIGHT,
+         N_("_Right"),
+         "<Control>r",
+         N_("Right Alignment"),
+         E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT }
+};
+
+static GtkRadioActionEntry core_mode_entries[] = {
+
+       { "mode-html",
+         NULL,
+         N_("_HTML"),
+         NULL,
+         N_("HTML editing mode"),
+         TRUE },       /* e_html_editor_view_set_html_mode */
+
+       { "mode-plain",
+         NULL,
+         N_("Plain _Text"),
+         NULL,
+         N_("Plain text editing mode"),
+         FALSE }       /* e_html_editor_view_set_html_mode */
+};
+
+static GtkRadioActionEntry core_style_entries[] = {
+
+       { "style-normal",
+         NULL,
+         N_("_Normal"),
+         "<Control>0",
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH },
+
+       { "style-h1",
+         NULL,
+         N_("Header _1"),
+         "<Control>1",
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1 },
+
+       { "style-h2",
+         NULL,
+         N_("Header _2"),
+         "<Control>2",
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2 },
+
+       { "style-h3",
+         NULL,
+         N_("Header _3"),
+         "<Control>3",
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3 },
+
+       { "style-h4",
+         NULL,
+         N_("Header _4"),
+         "<Control>4",
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4 },
+
+       { "style-h5",
+         NULL,
+         N_("Header _5"),
+         "<Control>5",
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5 },
+
+       { "style-h6",
+         NULL,
+         N_("Header _6"),
+         "<Control>6",
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6 },
+
+        { "style-preformat",
+          NULL,
+          N_("_Preformatted"),
+          "<Control>7",
+          NULL,
+          E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE },
+
+       { "style-address",
+         NULL,
+         N_("A_ddress"),
+         "<Control>8",
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS },
+
+        { "style-blockquote",
+          NULL,
+          N_("_Blockquote"),
+          "<Control>9",
+          NULL,
+          E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE },
+
+       { "style-list-bullet",
+         NULL,
+         N_("_Bulleted List"),
+         NULL,
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST },
+
+       { "style-list-roman",
+         NULL,
+         N_("_Roman Numeral List"),
+         NULL,
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN },
+
+       { "style-list-number",
+         NULL,
+         N_("Numbered _List"),
+         NULL,
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST },
+
+       { "style-list-alpha",
+         NULL,
+         N_("_Alphabetical List"),
+         NULL,
+         NULL,
+         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA }
+};
+
+/*****************************************************************************
+ * Core Actions (HTML only)
+ *
+ * These actions are only enabled in HTML mode.
+ *****************************************************************************/
+
+static GtkActionEntry html_entries[] = {
+
+       { "insert-image",
+         "insert-image",
+         N_("_Image..."),
+         NULL,
+         N_("Insert Image"),
+         G_CALLBACK (action_insert_image_cb) },
+
+       { "insert-link",
+         "insert-link",
+         N_("_Link..."),
+         NULL,
+         N_("Insert Link"),
+         G_CALLBACK (action_insert_link_cb) },
+
+       { "insert-rule",
+         "stock_insert-rule",
+         /* Translators: 'Rule' here means a horizontal line in an HTML text */
+         N_("_Rule..."),
+         NULL,
+         /* Translators: 'Rule' here means a horizontal line in an HTML text */
+         N_("Insert Rule"),
+         G_CALLBACK (action_insert_rule_cb) },
+
+       { "insert-table",
+         "stock_insert-table",
+         N_("_Table..."),
+         NULL,
+         N_("Insert Table"),
+         G_CALLBACK (action_insert_table_cb) },
+
+       { "properties-cell",
+         NULL,
+         N_("_Cell..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_cell_cb) },
+
+       { "properties-image",
+         NULL,
+         N_("_Image..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_image_cb) },
+
+       { "properties-link",
+         NULL,
+         N_("_Link..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_link_cb) },
+
+       { "properties-page",
+         NULL,
+         N_("Pa_ge..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_page_cb) },
+
+       { "properties-rule",
+         NULL,
+         /* Translators: 'Rule' here means a horizontal line in an HTML text */
+         N_("_Rule..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_rule_cb) },
+
+       { "properties-table",
+         NULL,
+         N_("_Table..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_table_cb) },
+
+       /* Menus */
+
+       { "font-size-menu",
+         NULL,
+         N_("Font _Size"),
+         NULL,
+         NULL,
+         NULL },
+
+       { "font-style-menu",
+         NULL,
+         N_("_Font Style"),
+         NULL,
+         NULL,
+         NULL },
+};
+
+static GtkToggleActionEntry html_toggle_entries[] = {
+
+       { "bold",
+         GTK_STOCK_BOLD,
+         N_("_Bold"),
+         "<Control>b",
+         N_("Bold"),
+         NULL,
+         FALSE },
+
+       { "italic",
+         GTK_STOCK_ITALIC,
+         N_("_Italic"),
+         "<Control>i",
+         N_("Italic"),
+         NULL,
+         FALSE },
+
+       { "monospaced",
+         "stock_text-monospaced",
+         N_("_Plain Text"),
+         "<Control>t",
+         N_("Plain Text"),
+         NULL,
+         FALSE },
+
+       { "strikethrough",
+         GTK_STOCK_STRIKETHROUGH,
+         N_("_Strikethrough"),
+         NULL,
+         N_("Strikethrough"),
+         NULL,
+         FALSE },
+
+       { "underline",
+         GTK_STOCK_UNDERLINE,
+         N_("_Underline"),
+         "<Control>u",
+         N_("Underline"),
+         NULL,
+         FALSE }
+};
+
+static GtkRadioActionEntry html_size_entries[] = {
+
+       { "size-minus-two",
+         NULL,
+         /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as 
possible. */
+         N_("-2"),
+         NULL,
+         NULL,
+         E_HTML_EDITOR_SELECTION_FONT_SIZE_TINY },
+
+       { "size-minus-one",
+         NULL,
+         /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as 
possible. */
+         N_("-1"),
+         NULL,
+         NULL,
+         E_HTML_EDITOR_SELECTION_FONT_SIZE_SMALL },
+
+       { "size-plus-zero",
+         NULL,
+         /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as 
possible. */
+         N_("+0"),
+         NULL,
+         NULL,
+         E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL },
+
+       { "size-plus-one",
+         NULL,
+         /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as 
possible. */
+         N_("+1"),
+         NULL,
+         NULL,
+         E_HTML_EDITOR_SELECTION_FONT_SIZE_BIG },
+
+       { "size-plus-two",
+         NULL,
+         /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as 
possible. */
+         N_("+2"),
+         NULL,
+         NULL,
+         E_HTML_EDITOR_SELECTION_FONT_SIZE_BIGGER },
+
+       { "size-plus-three",
+         NULL,
+         /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as 
possible. */
+         N_("+3"),
+         NULL,
+         NULL,
+         E_HTML_EDITOR_SELECTION_FONT_SIZE_LARGE },
+
+       { "size-plus-four",
+         NULL,
+         /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as 
possible. */
+         N_("+4"),
+         NULL,
+         NULL,
+         E_HTML_EDITOR_SELECTION_FONT_SIZE_VERY_LARGE }
+};
+
+/*****************************************************************************
+ * Context Menu Actions
+ *
+ * These require separate action entries so we can toggle their visiblity
+ * rather than their sensitivity as we do with main menu / toolbar actions.
+ * Note that some of these actions use the same callback function as their
+ * non-context sensitive counterparts.
+ *****************************************************************************/
+
+static GtkActionEntry context_entries[] = {
+
+       { "context-delete-cell",
+         NULL,
+         N_("Cell Contents"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_context_delete_cell_cb) },
+
+       { "context-delete-column",
+         NULL,
+         N_("Column"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_context_delete_column_cb) },
+
+       { "context-delete-row",
+         NULL,
+         N_("Row"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_context_delete_row_cb) },
+
+       { "context-delete-table",
+         NULL,
+         N_("Table"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_context_delete_table_cb) },
+
+       /* Menus */
+
+       { "context-delete-table-menu",
+         NULL,
+         /* Translators: Popup menu item caption, containing all the Delete options for a table */
+         N_("Table Delete"),
+         NULL,
+         NULL,
+         NULL },
+
+       { "context-input-methods-menu",
+         NULL,
+         N_("Input Methods"),
+         NULL,
+         NULL,
+         NULL },
+
+       { "context-insert-table-menu",
+         NULL,
+         /* Translators: Popup menu item caption, containing all the Insert options for a table */
+         N_("Table Insert"),
+         NULL,
+         NULL,
+         NULL },
+
+       { "context-properties-menu",
+         NULL,
+         N_("Properties"),
+         NULL,
+         NULL,
+         NULL },
+};
+
+/*****************************************************************************
+ * Context Menu Actions (HTML only)
+ *
+ * These actions are never visible in plain-text mode.  Note that some
+ * of them use the same callback function as their non-context sensitive
+ * counterparts.
+ *****************************************************************************/
+
+static GtkActionEntry html_context_entries[] = {
+
+       { "context-insert-column-after",
+         NULL,
+         N_("Column After"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_context_insert_column_after_cb) },
+
+       { "context-insert-column-before",
+         NULL,
+         N_("Column Before"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_context_insert_column_before_cb) },
+
+       { "context-insert-link",
+         NULL,
+         N_("Insert _Link"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_insert_link_cb) },
+
+       { "context-insert-row-above",
+         NULL,
+         N_("Row Above"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_context_insert_row_above_cb) },
+
+       { "context-insert-row-below",
+         NULL,
+         N_("Row Below"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_context_insert_row_below_cb) },
+
+       { "context-insert-table",
+         NULL,
+         N_("Table"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_insert_table_cb) },
+
+       { "context-properties-cell",
+         NULL,
+         N_("Cell..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_cell_cb) },
+
+       { "context-properties-image",
+         NULL,
+         N_("Image..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_image_cb) },
+
+       { "context-properties-link",
+         NULL,
+         N_("Link..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_link_cb) },
+
+       { "context-properties-page",
+         NULL,
+         N_("Page..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_page_cb) },
+
+       { "context-properties-paragraph",
+         NULL,
+         N_("Paragraph..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_paragraph_cb) },
+
+       { "context-properties-rule",
+         NULL,
+         /* Translators: 'Rule' here means a horizontal line in an HTML text */
+         N_("Rule..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_rule_cb) },
+
+       { "context-properties-table",
+         NULL,
+         N_("Table..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_table_cb) },
+
+       { "context-properties-text",
+         NULL,
+         N_("Text..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_properties_text_cb) },
+
+       { "context-remove-link",
+         NULL,
+         N_("Remove Link"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_context_remove_link_cb) }
+};
+
+/*****************************************************************************
+ * Context Menu Actions (spell checking only)
+ *
+ * These actions are only visible when the word underneath the cursor is
+ * misspelled.
+ *****************************************************************************/
+
+static GtkActionEntry spell_context_entries[] = {
+
+       { "context-spell-add",
+         NULL,
+         N_("Add Word to Dictionary"),
+         NULL,
+         NULL,
+          G_CALLBACK (action_context_spell_add_cb) },
+
+       { "context-spell-ignore",
+         NULL,
+         N_("Ignore Misspelled Word"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_context_spell_ignore_cb) },
+
+       { "context-spell-add-menu",
+         NULL,
+         N_("Add Word To"),
+         NULL,
+         NULL,
+         NULL },
+
+       /* Menus */
+
+       { "context-more-suggestions-menu",
+         NULL,
+         N_("More Suggestions"),
+         NULL,
+         NULL,
+         NULL }
+};
+
+static void
+editor_actions_setup_languages_menu (EHTMLEditor *editor)
+{
+       ESpellChecker *checker;
+       EHTMLEditorView *view;
+       GtkUIManager *manager;
+       GtkActionGroup *action_group;
+       GList *list, *link;
+       guint merge_id;
+
+       manager = editor->priv->manager;
+       action_group = editor->priv->language_actions;
+       view = e_html_editor_get_view (editor);
+       checker = e_html_editor_view_get_spell_checker (view);
+       merge_id = gtk_ui_manager_new_merge_id (manager);
+
+       list = e_spell_checker_list_available_dicts (checker);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               ESpellDictionary *dictionary = link->data;
+               GtkToggleAction *action;
+               gboolean active;
+
+               action = gtk_toggle_action_new (
+                       e_spell_dictionary_get_code (dictionary),
+                       e_spell_dictionary_get_name (dictionary),
+                       NULL, NULL);
+
+               /* Do this BEFORE connecting to the "toggled" signal.
+                * We're not prepared to invoke the signal handler yet.
+                * The "Add Word To" actions have not yet been added. */
+               active = e_spell_checker_get_language_active (
+                       checker, e_spell_dictionary_get_code (dictionary));
+               gtk_toggle_action_set_active (action, active);
+
+               g_signal_connect (
+                       action, "toggled",
+                       G_CALLBACK (action_language_cb), editor);
+
+               gtk_action_group_add_action (
+                       action_group, GTK_ACTION (action));
+
+               g_object_unref (action);
+
+               gtk_ui_manager_add_ui (
+                       manager, merge_id,
+                       "/main-menu/edit-menu/language-menu",
+                       e_spell_dictionary_get_code (dictionary),
+                       e_spell_dictionary_get_code (dictionary),
+                       GTK_UI_MANAGER_AUTO, FALSE);
+       }
+
+       g_list_free (list);
+}
+
+static void
+editor_actions_setup_spell_check_menu (EHTMLEditor *editor)
+{
+       ESpellChecker *checker;
+       GtkUIManager *manager;
+       GtkActionGroup *action_group;
+       GList *available_dicts, *iter;
+       guint merge_id;
+
+       manager = editor->priv->manager;
+       action_group = editor->priv->spell_check_actions;;
+       checker = e_html_editor_view_get_spell_checker (editor->priv->html_editor_view);
+       available_dicts = e_spell_checker_list_available_dicts (checker);
+       merge_id = gtk_ui_manager_new_merge_id (manager);
+
+       for (iter = available_dicts; iter; iter = iter->next) {
+               ESpellDictionary *dictionary = iter->data;
+               GtkAction *action;
+               const gchar *code;
+               const gchar *name;
+               gchar *action_label;
+               gchar *action_name;
+
+               code = e_spell_dictionary_get_code (dictionary);
+               name = e_spell_dictionary_get_name (dictionary);
+
+               /* Add a suggestion menu. */
+               action_name = g_strdup_printf (
+                       "context-spell-suggest-%s-menu", code);
+
+               action = gtk_action_new (action_name, name, NULL, NULL);
+               gtk_action_group_add_action (action_group, action);
+               g_object_unref (action);
+
+               gtk_ui_manager_add_ui (
+                       manager, merge_id,
+                       "/context-menu/context-spell-suggest",
+                       action_name, action_name,
+                       GTK_UI_MANAGER_MENU, FALSE);
+
+               g_free (action_name);
+
+               /* Add an item to the "Add Word To" menu. */
+               action_name = g_strdup_printf ("context-spell-add-%s", code);
+               /* Translators: %s will be replaced with the actual dictionary
+                * name, where a user can add a word to. This is part of an
+                * "Add Word To" submenu. */
+               action_label = g_strdup_printf (_("%s Dictionary"), name);
+
+               action = gtk_action_new (
+                       action_name, action_label, NULL, NULL);
+
+               g_signal_connect (
+                       action, "activate",
+                       G_CALLBACK (action_context_spell_add_cb), editor);
+
+               /* Visibility is dependent on whether the
+                * corresponding language action is active. */
+               gtk_action_set_visible (action, FALSE);
+
+               gtk_action_group_add_action (action_group, action);
+
+               g_object_unref (action);
+
+               gtk_ui_manager_add_ui (
+                       manager, merge_id,
+                       "/context-menu/context-spell-add-menu",
+                       action_name, action_name,
+                       GTK_UI_MANAGER_AUTO, FALSE);
+
+               g_free (action_label);
+               g_free (action_name);
+       }
+
+       g_list_free (available_dicts);
+}
+
+void
+editor_actions_init (EHTMLEditor *editor)
+{
+       GtkAction *action;
+       GtkActionGroup *action_group;
+       GtkUIManager *manager;
+       const gchar *domain;
+       EHTMLEditorView *view;
+       GSettings *settings;
+
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+       manager = e_html_editor_get_ui_manager (editor);
+       domain = GETTEXT_PACKAGE;
+       view = e_html_editor_get_view (editor);
+
+       /* Core Actions */
+       action_group = editor->priv->core_actions;
+       gtk_action_group_set_translation_domain (action_group, domain);
+       gtk_action_group_add_actions (
+               action_group, core_entries,
+               G_N_ELEMENTS (core_entries), editor);
+       gtk_action_group_add_radio_actions (
+               action_group, core_justify_entries,
+               G_N_ELEMENTS (core_justify_entries),
+               E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
+               NULL, NULL);
+       gtk_action_group_add_radio_actions (
+               action_group, core_mode_entries,
+               G_N_ELEMENTS (core_mode_entries),
+               TRUE,
+               G_CALLBACK (action_mode_cb), editor);
+       gtk_action_group_add_radio_actions (
+               action_group, core_style_entries,
+               G_N_ELEMENTS (core_style_entries),
+               E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH,
+               NULL, NULL);
+       gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+       action = gtk_action_group_get_action (action_group, "mode-html");
+       g_object_bind_property (
+               view, "html-mode",
+               action, "current-value",
+               G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+       /* Synchronize wiget mode with the buttons */
+       e_html_editor_view_set_html_mode (view, TRUE);
+
+       /* Face Action */
+       action = e_emoticon_action_new (
+               "insert-emoticon", _("_Emoticon"),
+               _("Insert Emoticon"), NULL);
+       g_object_set (action, "icon-name", "face-smile", NULL);
+       g_signal_connect (
+               action, "item-activated",
+               G_CALLBACK (action_insert_emoticon_cb), editor);
+       gtk_action_group_add_action (action_group, action);
+       g_object_unref (action);
+
+       /* Core Actions (HTML only) */
+       action_group = editor->priv->html_actions;
+       gtk_action_group_set_translation_domain (action_group, domain);
+       gtk_action_group_add_actions (
+               action_group, html_entries,
+               G_N_ELEMENTS (html_entries), editor);
+       gtk_action_group_add_toggle_actions (
+               action_group, html_toggle_entries,
+               G_N_ELEMENTS (html_toggle_entries), editor);
+       gtk_action_group_add_radio_actions (
+               action_group, html_size_entries,
+               G_N_ELEMENTS (html_size_entries),
+               E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL,
+               NULL, NULL);
+       gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+       /* Context Menu Actions */
+       action_group = editor->priv->context_actions;
+       gtk_action_group_set_translation_domain (action_group, domain);
+       gtk_action_group_add_actions (
+               action_group, context_entries,
+               G_N_ELEMENTS (context_entries), editor);
+       gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+       /* Context Menu Actions (HTML only) */
+       action_group = editor->priv->html_context_actions;
+       gtk_action_group_set_translation_domain (action_group, domain);
+       gtk_action_group_add_actions (
+               action_group, html_context_entries,
+               G_N_ELEMENTS (html_context_entries), editor);
+       gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+       /* Context Menu Actions (spell check only) */
+       action_group = editor->priv->spell_check_actions;
+       gtk_action_group_set_translation_domain (action_group, domain);
+       gtk_action_group_add_actions (
+               action_group, spell_context_entries,
+               G_N_ELEMENTS (spell_context_entries), editor);
+       gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+       /* Language actions are generated dynamically. */
+       editor_actions_setup_languages_menu (editor);
+       action_group = editor->priv->language_actions;
+       gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+       /* Some spell check actions are generated dynamically. */
+       action_group = editor->priv->suggestion_actions;
+       editor_actions_setup_spell_check_menu (editor);
+       gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+       /* Do this after all language actions are initialized. */
+       editor_update_static_spell_actions (editor);
+
+       /* Fine Tuning */
+
+       g_object_set (
+               G_OBJECT (ACTION (SHOW_FIND)),
+               "short-label", _("_Find"), NULL);
+       g_object_set (
+               G_OBJECT (ACTION (SHOW_REPLACE)),
+               "short-label", _("Re_place"), NULL);
+       g_object_set (
+               G_OBJECT (ACTION (INSERT_IMAGE)),
+               "short-label", _("_Image"), NULL);
+       g_object_set (
+               G_OBJECT (ACTION (INSERT_LINK)),
+               "short-label", _("_Link"), NULL);
+       g_object_set (
+               G_OBJECT (ACTION (INSERT_RULE)),
+               /* Translators: 'Rule' here means a horizontal line in an HTML text */
+               "short-label", _("_Rule"), NULL);
+       g_object_set (
+               G_OBJECT (ACTION (INSERT_TABLE)),
+               "short-label", _("_Table"), NULL);
+
+       gtk_action_set_sensitive (ACTION (UNINDENT), FALSE);
+       gtk_action_set_sensitive (ACTION (FIND_AGAIN), FALSE);
+       gtk_action_set_sensitive (ACTION (SPELL_CHECK), FALSE);
+
+       g_object_bind_property (
+               view, "can-redo",
+               ACTION (REDO), "sensitive",
+               G_BINDING_SYNC_CREATE);
+       g_object_bind_property (
+               view, "can-undo",
+               ACTION (UNDO), "sensitive",
+               G_BINDING_SYNC_CREATE);
+       g_object_bind_property (
+               view, "can-copy",
+               ACTION (COPY), "sensitive",
+               G_BINDING_SYNC_CREATE);
+       g_object_bind_property (
+               view, "can-cut",
+               ACTION (CUT), "sensitive",
+               G_BINDING_SYNC_CREATE);
+       g_object_bind_property (
+               view, "can-paste",
+               ACTION (PASTE), "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       /* This is connected to JUSTIFY_LEFT action only, but
+        * it automatically applies on all actions in the group. */
+       g_object_bind_property (
+               editor->priv->selection, "alignment",
+               ACTION (JUSTIFY_LEFT), "current-value",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_object_bind_property (
+               editor->priv->selection, "bold",
+               ACTION (BOLD), "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_object_bind_property (
+               editor->priv->selection, "font-size",
+               ACTION (FONT_SIZE_GROUP), "current-value",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_object_bind_property (
+               editor->priv->selection, "block-format",
+               ACTION (STYLE_NORMAL), "current-value",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_object_bind_property (
+               editor->priv->selection, "indented",
+               ACTION (UNINDENT), "sensitive",
+               G_BINDING_SYNC_CREATE);
+       g_object_bind_property (
+               editor->priv->selection, "italic",
+               ACTION (ITALIC), "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_object_bind_property (
+               editor->priv->selection, "monospaced",
+               ACTION (MONOSPACED), "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_object_bind_property (
+               editor->priv->selection, "strikethrough",
+               ACTION (STRIKETHROUGH), "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_object_bind_property (
+               editor->priv->selection, "underline",
+               ACTION (UNDERLINE), "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+       /* Disable all actions and toolbars when editor is not editable */
+       g_object_bind_property (
+               view, "editable",
+               editor->priv->core_actions, "sensitive",
+               G_BINDING_SYNC_CREATE);
+       g_object_bind_property (
+               view, "editable",
+               editor->priv->html_actions, "sensitive",
+               G_BINDING_SYNC_CREATE);
+       g_object_bind_property (
+               view, "editable",
+               editor->priv->spell_check_actions, "sensitive",
+               G_BINDING_SYNC_CREATE);
+       g_object_bind_property (
+               view, "editable",
+               editor->priv->suggestion_actions, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       settings = g_settings_new ("org.gnome.evolution.mail");
+       gtk_action_set_visible (
+               ACTION (WEBKIT_INSPECTOR),
+               g_settings_get_boolean (settings, "composer-developer-mode"));
+       g_object_unref (settings);
+}
diff --git a/e-util/e-html-editor-actions.h b/e-util/e-html-editor-actions.h
new file mode 100644
index 0000000..8999add
--- /dev/null
+++ b/e-util/e-html-editor-actions.h
@@ -0,0 +1,155 @@
+/* e-html-editor-actions.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_ACTIONS_H
+#define E_HTML_EDITOR_ACTIONS_H
+
+#define E_HTML_EDITOR_ACTION(editor, name) \
+       (e_html_editor_get_action (E_HTML_EDITOR (editor), (name)))
+
+#define E_HTML_EDITOR_ACTION_BOLD(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "bold")
+#define E_HTML_EDITOR_ACTION_CONTEXT_DELETE_CELL(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-delete-cell")
+#define E_HTML_EDITOR_ACTION_CONTEXT_DELETE_COLUMN(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-delete-column")
+#define E_HTML_EDITOR_ACTION_CONTEXT_DELETE_ROW(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-delete-row")
+#define E_HTML_EDITOR_ACTION_CONTEXT_DELETE_TABLE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-delete-table")
+#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_COLUMN_AFTER(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-insert-column-after")
+#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_COLUMN_BEFORE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-insert-column-before")
+#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_ROW_ABOVE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-insert-row-above")
+#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_ROW_BELOW(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-insert-row-below")
+#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_TABLE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-insert-table")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_CELL(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-properties-cell")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_IMAGE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-properties-image")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_LINK(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-properties-link")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_PARAGRAPH(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-properties-paragraph")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_RULE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-properties-rule")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_TABLE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-properties-table")
+#define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_TEXT(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-properties-text")
+#define E_HTML_EDITOR_ACTION_CONTEXT_REMOVE_LINK(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-remove-link")
+#define E_HTML_EDITOR_ACTION_CONTEXT_SPELL_ADD(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-spell-add")
+#define E_HTML_EDITOR_ACTION_CONTEXT_SPELL_ADD_MENU(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-spell-add-menu")
+#define E_HTML_EDITOR_ACTION_CONTEXT_SPELL_IGNORE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-spell-ignore")
+#define E_HTML_EDITOR_ACTION_COPY(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "copy")
+#define E_HTML_EDITOR_ACTION_CUT(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "cut")
+#define E_HTML_EDITOR_ACTION_EDIT_MENU(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "edit-menu")
+#define E_HTML_EDITOR_ACTION_FIND_AGAIN(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "find-again")
+#define E_HTML_EDITOR_ACTION_FONT_SIZE_GROUP(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "size-plus-zero")
+#define E_HTML_EDITOR_ACTION_FORMAT_MENU(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "format-menu")
+#define E_HTML_EDITOR_ACTION_FORMAT_TEXT(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "format-text")
+#define E_HTML_EDITOR_ACTION_INSERT_IMAGE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "insert-image")
+#define E_HTML_EDITOR_ACTION_INSERT_LINK(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "insert-link")
+#define E_HTML_EDITOR_ACTION_INSERT_MENU(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "insert-menu")
+#define E_HTML_EDITOR_ACTION_INSERT_RULE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "insert-rule")
+#define E_HTML_EDITOR_ACTION_INSERT_TABLE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "insert-table")
+#define E_HTML_EDITOR_ACTION_ITALIC(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "italic")
+#define E_HTML_EDITOR_ACTION_JUSTIFY_CENTER(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "justify-center")
+#define E_HTML_EDITOR_ACTION_JUSTIFY_LEFT(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "justify-left")
+#define E_HTML_EDITOR_ACTION_JUSTIFY_RIGHT(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "justify-right")
+#define E_HTML_EDITOR_ACTION_MODE_HTML(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "mode-html")
+#define E_HTML_EDITOR_ACTION_MODE_PLAIN(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "mode-plain")
+#define E_HTML_EDITOR_ACTION_MONOSPACED(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "monospaced")
+#define E_HTML_EDITOR_ACTION_PASTE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "paste")
+#define E_HTML_EDITOR_ACTION_PROPERTIES_RULE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "properties-rule")
+#define E_HTML_EDITOR_ACTION_PROPERTIES_TABLE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "properties-table")
+#define E_HTML_EDITOR_ACTION_REDO(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "redo")
+#define E_HTML_EDITOR_ACTION_SHOW_FIND(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "show-find")
+#define E_HTML_EDITOR_ACTION_SHOW_REPLACE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "show-replace")
+#define E_HTML_EDITOR_ACTION_SIZE_PLUS_ZERO(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "size-plus-zero")
+#define E_HTML_EDITOR_ACTION_SPELL_CHECK(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "spell-check")
+#define E_HTML_EDITOR_ACTION_STRIKETHROUGH(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "strikethrough")
+#define E_HTML_EDITOR_ACTION_STYLE_ADDRESS(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "style-address")
+#define E_HTML_EDITOR_ACTION_STYLE_H1(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "style-h1")
+#define E_HTML_EDITOR_ACTION_STYLE_H2(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "style-h2")
+#define E_HTML_EDITOR_ACTION_STYLE_H3(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "style-h3")
+#define E_HTML_EDITOR_ACTION_STYLE_H4(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "style-h4")
+#define E_HTML_EDITOR_ACTION_STYLE_H5(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "style-h5")
+#define E_HTML_EDITOR_ACTION_STYLE_H6(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "style-h6")
+#define E_HTML_EDITOR_ACTION_STYLE_NORMAL(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "style-normal")
+#define E_HTML_EDITOR_ACTION_TEST_URL(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "test-url")
+#define E_HTML_EDITOR_ACTION_UNDERLINE(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "underline")
+#define E_HTML_EDITOR_ACTION_UNDO(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "undo")
+#define E_HTML_EDITOR_ACTION_UNINDENT(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "unindent")
+#define E_HTML_EDITOR_ACTION_WEBKIT_INSPECTOR(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "webkit-inspector")
+
+#endif /* E_HTML_EDITOR_ACTIONS_H */
diff --git a/e-util/e-html-editor-cell-dialog.c b/e-util/e-html-editor-cell-dialog.c
new file mode 100644
index 0000000..2b48728
--- /dev/null
+++ b/e-util/e-html-editor-cell-dialog.c
@@ -0,0 +1,872 @@
+/*
+ * e-html-editor-cell-dialog.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-cell-dialog.h"
+
+#include <glib/gi18n-lib.h>
+#include <stdlib.h>
+
+#include "e-color-combo.h"
+#include "e-html-editor-utils.h"
+#include "e-image-chooser-dialog.h"
+#include "e-misc-utils.h"
+
+#define E_HTML_EDITOR_CELL_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_CELL_DIALOG, EHTMLEditorCellDialogPrivate))
+
+struct _EHTMLEditorCellDialogPrivate {
+       GtkWidget *scope_cell_button;
+       GtkWidget *scope_table_button;
+       GtkWidget *scope_row_button;
+       GtkWidget *scope_column_button;
+
+       GtkWidget *halign_combo;
+       GtkWidget *valign_combo;
+
+       GtkWidget *wrap_text_check;
+       GtkWidget *header_style_check;
+
+       GtkWidget *width_check;
+       GtkWidget *width_edit;
+       GtkWidget *width_units;
+
+       GtkWidget *row_span_edit;
+       GtkWidget *col_span_edit;
+
+       GtkWidget *background_color_picker;
+       GtkWidget *background_image_chooser;
+
+       WebKitDOMElement *cell;
+       guint scope;
+};
+
+enum {
+       SCOPE_CELL,
+       SCOPE_ROW,
+       SCOPE_COLUMN,
+       SCOPE_TABLE
+} DialogScope;
+
+static GdkRGBA white = { 1, 1, 1, 1 };
+
+typedef void (*DOMStrFunc) (WebKitDOMHTMLTableCellElement *cell, const gchar *val, gpointer user_data);
+typedef void (*DOMUlongFunc) (WebKitDOMHTMLTableCellElement *cell, gulong val, gpointer user_data);
+typedef void (*DOMBoolFunc) (WebKitDOMHTMLTableCellElement *cell, gboolean val, gpointer user_data);
+
+G_DEFINE_TYPE (
+       EHTMLEditorCellDialog,
+       e_html_editor_cell_dialog,
+       E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+call_cell_dom_func (WebKitDOMHTMLTableCellElement *cell,
+                    gpointer func,
+                    GValue *value,
+                    gpointer user_data)
+{
+       if (G_VALUE_HOLDS_STRING (value)) {
+               DOMStrFunc f = func;
+               f (cell, g_value_get_string (value), user_data);
+       } else if (G_VALUE_HOLDS_ULONG (value)) {
+               DOMUlongFunc f = func;
+               f (cell, g_value_get_ulong (value), user_data);
+       } else if (G_VALUE_HOLDS_BOOLEAN (value)) {
+               DOMBoolFunc f = func;
+               f (cell, g_value_get_boolean (value), user_data);
+       }
+}
+
+static void
+for_each_cell_do (WebKitDOMElement *row,
+                  gpointer func,
+                  GValue *value,
+                  gpointer user_data)
+{
+       WebKitDOMHTMLCollection *cells;
+       gulong ii, length;
+       cells = webkit_dom_html_table_row_element_get_cells (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+       length = webkit_dom_html_collection_get_length (cells);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *cell;
+               cell = webkit_dom_html_collection_item (cells, ii);
+               if (!cell) {
+                       continue;
+               }
+
+               call_cell_dom_func (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell), func, value, user_data);
+       }
+}
+
+static void
+html_editor_cell_dialog_set_attribute (EHTMLEditorCellDialog *dialog,
+                                       gpointer func,
+                                       GValue *value,
+                                       gpointer user_data)
+{
+       if (dialog->priv->scope == SCOPE_CELL) {
+
+               call_cell_dom_func (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell),
+                       func, value, user_data);
+
+       } else if (dialog->priv->scope == SCOPE_COLUMN) {
+               gulong index, ii, length;
+               WebKitDOMElement *table;
+               WebKitDOMHTMLCollection *rows;
+
+               index = webkit_dom_html_table_cell_element_get_cell_index (
+                               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+               table = e_html_editor_dom_node_find_parent_element (
+                               WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE");
+               if (!table) {
+                       return;
+               }
+
+               rows = webkit_dom_html_table_element_get_rows (
+                               WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
+               length = webkit_dom_html_collection_get_length (rows);
+               for (ii = 0; ii < length; ii++) {
+                       WebKitDOMNode *row, *cell;
+                       WebKitDOMHTMLCollection *cells;
+
+                       row = webkit_dom_html_collection_item (rows, ii);
+                       cells = webkit_dom_html_table_row_element_get_cells (
+                                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+                       cell = webkit_dom_html_collection_item (cells, index);
+                       if (!cell) {
+                               continue;
+                       }
+
+                       call_cell_dom_func (
+                               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell),
+                               func, value, user_data);
+               }
+
+       } else if (dialog->priv->scope == SCOPE_ROW) {
+               WebKitDOMElement *row;
+
+               row = e_html_editor_dom_node_find_parent_element (
+                               WEBKIT_DOM_NODE (dialog->priv->cell), "TR");
+               if (!row) {
+                       return;
+               }
+
+               for_each_cell_do (row, func, value, user_data);
+
+       } else if (dialog->priv->scope == SCOPE_TABLE) {
+               gulong ii, length;
+               WebKitDOMElement *table;
+               WebKitDOMHTMLCollection *rows;
+
+               table = e_html_editor_dom_node_find_parent_element (
+                               WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE");
+               if (!table) {
+                       return;
+               }
+
+               rows = webkit_dom_html_table_element_get_rows (
+                               WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
+               length = webkit_dom_html_collection_get_length (rows);
+               for (ii = 0; ii < length; ii++) {
+                       WebKitDOMNode *row;
+
+                       row = webkit_dom_html_collection_item (rows, ii);
+                       if (!row) {
+                               continue;
+                       }
+
+                       for_each_cell_do (
+                               WEBKIT_DOM_ELEMENT (row), func, value, user_data);
+               }
+       }
+}
+
+static void
+html_editor_cell_dialog_set_scope (EHTMLEditorCellDialog *dialog)
+{
+       if (gtk_toggle_button_get_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->scope_cell_button))) {
+
+               dialog->priv->scope = SCOPE_CELL;
+
+       } else if (gtk_toggle_button_get_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->scope_row_button))) {
+
+               dialog->priv->scope = SCOPE_ROW;
+
+       } else if (gtk_toggle_button_get_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->scope_column_button))) {
+
+               dialog->priv->scope = SCOPE_COLUMN;
+
+       } else if (gtk_toggle_button_get_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->scope_table_button))) {
+
+               dialog->priv->scope = SCOPE_TABLE;
+
+       }
+}
+
+static  void
+html_editor_cell_dialog_set_valign (EHTMLEditorCellDialog *dialog)
+{
+       GValue val = { 0 };
+
+       g_value_init (&val, G_TYPE_STRING);
+       g_value_set_string (
+               &val,
+               gtk_combo_box_get_active_id (
+                       GTK_COMBO_BOX (dialog->priv->valign_combo)));
+
+       html_editor_cell_dialog_set_attribute (
+               dialog, webkit_dom_html_table_cell_element_set_v_align, &val, NULL);
+
+       g_value_unset (&val);
+}
+
+static void
+html_editor_cell_dialog_set_halign (EHTMLEditorCellDialog *dialog)
+{
+       GValue val = { 0 };
+
+       g_value_init (&val, G_TYPE_STRING);
+       g_value_set_string (
+               &val,
+               gtk_combo_box_get_active_id (
+                       GTK_COMBO_BOX (dialog->priv->halign_combo)));
+
+       html_editor_cell_dialog_set_attribute (
+               dialog, webkit_dom_html_table_cell_element_set_align, &val, NULL);
+
+       g_value_unset (&val);
+}
+
+static void
+html_editor_cell_dialog_set_wrap_text (EHTMLEditorCellDialog *dialog)
+{
+       GValue val = { 0 };
+
+       g_value_init (&val, G_TYPE_BOOLEAN);
+       g_value_set_boolean (
+               &val,
+               !gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->wrap_text_check)));
+
+       html_editor_cell_dialog_set_attribute (
+               dialog, webkit_dom_html_table_cell_element_set_no_wrap, &val, NULL);
+}
+
+static void
+cell_set_header_style (WebKitDOMHTMLTableCellElement *cell,
+                       gboolean header_style,
+                       gpointer user_data)
+{
+       EHTMLEditorCellDialog *dialog = user_data;
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *nodes;
+       WebKitDOMElement *new_cell;
+       gulong length, ii;
+       gchar *tagname;
+
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (cell));
+       tagname = webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (cell));
+
+       if (header_style && (g_ascii_strncasecmp (tagname, "TD", 2) == 0)) {
+
+               new_cell = webkit_dom_document_create_element (document, "TH", NULL);
+
+       } else if (!header_style && (g_ascii_strncasecmp (tagname, "TH", 2) == 0)) {
+
+               new_cell = webkit_dom_document_create_element (document, "TD", NULL);
+
+       } else {
+               g_free (tagname);
+               return;
+       }
+
+       /* Move all child nodes from cell to new_cell */
+       nodes = webkit_dom_node_get_child_nodes (WEBKIT_DOM_NODE (cell));
+       length = webkit_dom_node_list_get_length (nodes);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (nodes, ii);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (new_cell), node, NULL);
+       }
+
+       /* Insert new_cell before cell and remove cell */
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (cell)),
+               WEBKIT_DOM_NODE (new_cell),
+               WEBKIT_DOM_NODE (cell), NULL);
+
+       webkit_dom_node_remove_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (cell)),
+               WEBKIT_DOM_NODE (cell), NULL);
+
+       dialog->priv->cell = new_cell;
+
+       g_free (tagname);
+}
+
+static void
+html_editor_cell_dialog_set_header_style (EHTMLEditorCellDialog *dialog)
+{
+       GValue val = { 0 };
+
+       g_value_init (&val, G_TYPE_BOOLEAN);
+       g_value_set_boolean (
+               &val,
+               gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->header_style_check)));
+
+       html_editor_cell_dialog_set_attribute (
+               dialog, cell_set_header_style, &val, dialog);
+}
+
+static void
+html_editor_cell_dialog_set_width (EHTMLEditorCellDialog *dialog)
+{
+       GValue val = { 0 };
+       gchar *width;
+
+       if (!gtk_toggle_button_get_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->width_check))) {
+
+               width = g_strdup ("auto");
+       } else {
+
+               width = g_strdup_printf (
+                       "%d%s",
+                       gtk_spin_button_get_value_as_int (
+                               GTK_SPIN_BUTTON (dialog->priv->width_edit)),
+                       ((gtk_combo_box_get_active (
+                               GTK_COMBO_BOX (dialog->priv->width_units)) == 0) ?
+                                       "px" : "%"));
+       }
+
+       g_value_init (&val, G_TYPE_STRING);
+       g_value_take_string (&val, width);
+       html_editor_cell_dialog_set_attribute (
+               dialog, webkit_dom_html_table_cell_element_set_width, &val, NULL);
+
+       g_free (width);
+}
+
+static void
+html_editor_cell_dialog_set_column_span (EHTMLEditorCellDialog *dialog)
+{
+       GValue val = { 0 };
+
+       g_value_init (&val, G_TYPE_ULONG);
+       g_value_set_ulong (
+               &val,
+               gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->col_span_edit)));
+
+       html_editor_cell_dialog_set_attribute (
+               dialog, webkit_dom_html_table_cell_element_set_col_span, &val, NULL);
+}
+
+static void
+html_editor_cell_dialog_set_row_span (EHTMLEditorCellDialog *dialog)
+{
+       GValue val = { 0 };
+
+       g_value_init (&val, G_TYPE_ULONG);
+       g_value_set_ulong (
+               &val,
+               gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->row_span_edit)));
+
+       html_editor_cell_dialog_set_attribute (
+               dialog, webkit_dom_html_table_cell_element_set_row_span, &val, NULL);
+}
+
+static void
+html_editor_cell_dialog_set_background_color (EHTMLEditorCellDialog *dialog)
+{
+       gchar *color;
+       GdkRGBA rgba;
+       GValue val = { 0 };
+
+       e_color_combo_get_current_color (
+               E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
+       color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
+
+       g_value_init (&val, G_TYPE_STRING);
+       g_value_take_string (&val, color);
+
+       html_editor_cell_dialog_set_attribute (
+               dialog, webkit_dom_html_table_cell_element_set_bg_color, &val, NULL);
+
+       g_free (color);
+}
+
+static void
+cell_set_background_image (WebKitDOMHTMLTableCellElement *cell,
+                           const gchar *uri,
+                           gpointer user_data)
+{
+       if (!uri || !*uri) {
+               webkit_dom_element_remove_attribute (
+                       WEBKIT_DOM_ELEMENT (cell), "background");
+       } else {
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (cell), "background", uri, NULL);
+       }
+}
+
+static void
+html_editor_cell_dialog_set_background_image (EHTMLEditorCellDialog *dialog)
+{
+       const gchar *uri;
+       GValue val = { 0 };
+
+       uri = gtk_file_chooser_get_uri (
+               GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
+
+       g_value_init (&val, G_TYPE_STRING);
+       g_value_take_string (&val, (gchar *) uri);
+
+       html_editor_cell_dialog_set_attribute (
+               dialog, cell_set_background_image, &val, NULL);
+}
+
+static void
+html_editor_cell_dialog_show (GtkWidget *widget)
+{
+       EHTMLEditorCellDialog *dialog;
+       gchar *tmp;
+       GdkRGBA color;
+
+       dialog = E_HTML_EDITOR_CELL_DIALOG (widget);
+
+       gtk_toggle_button_set_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->scope_cell_button), TRUE);
+
+       tmp = webkit_dom_html_table_cell_element_get_align (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->halign_combo),
+               (tmp && *tmp) ? tmp : "left");
+       g_free (tmp);
+
+       tmp = webkit_dom_html_table_cell_element_get_v_align (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->valign_combo),
+               (tmp && *tmp) ? tmp : "middle");
+       g_free (tmp);
+
+       gtk_toggle_button_set_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->wrap_text_check),
+               !webkit_dom_html_table_cell_element_get_no_wrap (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)));
+
+       tmp = webkit_dom_element_get_tag_name (
+               WEBKIT_DOM_ELEMENT (dialog->priv->cell));
+       gtk_toggle_button_set_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->header_style_check),
+               (g_ascii_strncasecmp (tmp, "TH", 2) == 0));
+       g_free (tmp);
+
+       tmp = webkit_dom_html_table_cell_element_get_width (
+               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+       if (tmp && *tmp) {
+               gint val = atoi (tmp);
+               gtk_spin_button_set_value (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit), val);
+               gtk_toggle_button_set_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->width_check), TRUE);
+       } else {
+               gtk_spin_button_set_value (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit), 0);
+               gtk_toggle_button_set_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->width_check), FALSE);
+       }
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->width_units), "units-px");
+       g_free (tmp);
+
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->row_span_edit),
+               webkit_dom_html_table_cell_element_get_row_span (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)));
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->col_span_edit),
+               webkit_dom_html_table_cell_element_get_col_span (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)));
+
+       if (webkit_dom_element_has_attribute (
+               WEBKIT_DOM_ELEMENT (dialog->priv->cell), "background")) {
+               tmp = webkit_dom_element_get_attribute (
+                       WEBKIT_DOM_ELEMENT (dialog->priv->cell), "background");
+
+               gtk_file_chooser_set_uri (
+                       GTK_FILE_CHOOSER (dialog->priv->background_image_chooser),
+                       tmp);
+
+               g_free (tmp);
+       } else {
+               gtk_file_chooser_unselect_all (
+                       GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
+       }
+
+       tmp = webkit_dom_html_table_cell_element_get_bg_color (
+               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+       if (!tmp || *tmp) {
+               color = white;
+       }
+       if (gdk_rgba_parse (&color, tmp)) {
+               e_color_combo_set_current_color (
+                       E_COLOR_COMBO (dialog->priv->background_color_picker),
+                       &color);
+       } else {
+               e_color_combo_set_current_color (
+                       E_COLOR_COMBO (dialog->priv->background_color_picker),
+                       &white);
+       }
+       g_free (tmp);
+
+       GTK_WIDGET_CLASS (e_html_editor_cell_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_cell_dialog_class_init (EHTMLEditorCellDialogClass *class)
+{
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorCellDialogPrivate));
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->show = html_editor_cell_dialog_show;
+}
+
+static void
+e_html_editor_cell_dialog_init (EHTMLEditorCellDialog *dialog)
+{
+       GtkGrid *main_layout, *grid;
+       GtkWidget *widget;
+       GtkFileFilter *file_filter;
+
+       dialog->priv = E_HTML_EDITOR_CELL_DIALOG_GET_PRIVATE (dialog);
+
+       main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+       /* == Scope == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Scope</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Scope: cell */
+       widget = gtk_image_new_from_icon_name ("stock_select-cell", GTK_ICON_SIZE_BUTTON);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       widget = gtk_radio_button_new_with_mnemonic (NULL, _("C_ell"));
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       dialog->priv->scope_cell_button = widget;
+
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_cell_dialog_set_scope), dialog);
+
+       /* Scope: row */
+       widget = gtk_image_new_from_icon_name ("stock_select-row", GTK_ICON_SIZE_BUTTON);
+       gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+
+       widget = gtk_radio_button_new_with_mnemonic_from_widget (
+               GTK_RADIO_BUTTON (dialog->priv->scope_cell_button), _("_Row"));
+       gtk_grid_attach (grid, widget, 3, 0, 1, 1);
+       dialog->priv->scope_row_button = widget;
+
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_cell_dialog_set_scope), dialog);
+
+       /* Scope: table */
+       widget = gtk_image_new_from_icon_name ("stock_select-table", GTK_ICON_SIZE_BUTTON);
+       gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+       widget = gtk_radio_button_new_with_mnemonic_from_widget (
+               GTK_RADIO_BUTTON (dialog->priv->scope_cell_button), _("_Table"));
+       gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+       dialog->priv->scope_table_button = widget;
+
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_cell_dialog_set_scope), dialog);
+
+       /* Scope: column */
+       widget = gtk_image_new_from_icon_name ("stock_select-column", GTK_ICON_SIZE_BUTTON);
+       gtk_grid_attach (grid, widget, 2, 1, 1, 1);
+
+       widget = gtk_radio_button_new_with_mnemonic_from_widget (
+               GTK_RADIO_BUTTON (dialog->priv->scope_cell_button), _("Col_umn"));
+       gtk_grid_attach (grid, widget, 3, 1, 1, 1);
+       dialog->priv->scope_column_button = widget;
+
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_cell_dialog_set_scope), dialog);
+
+       /* == Alignment & Behavior == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Alignment &amp; Behavior</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Horizontal */
+       widget = gtk_combo_box_text_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "left", _("Left"));
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "center", _("Center"));
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "right", _("Right"));
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       dialog->priv->halign_combo = widget;
+
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_cell_dialog_set_halign), dialog);
+
+       widget = gtk_label_new_with_mnemonic (_("_Horizontal:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->halign_combo);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       /* Vertical */
+       widget = gtk_combo_box_text_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "top", _("Top"));
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "middle", _("Middle"));
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "bottom", _("Bottom"));
+       gtk_grid_attach (grid, widget, 3, 0, 1, 1);
+       dialog->priv->valign_combo = widget;
+
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_cell_dialog_set_valign), dialog);
+
+       widget = gtk_label_new_with_mnemonic (_("_Vertical:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->valign_combo);
+       gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+
+       /* Wrap Text */
+       widget = gtk_check_button_new_with_mnemonic (_("_Wrap Text"));
+       dialog->priv->wrap_text_check = widget;
+
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_cell_dialog_set_wrap_text), dialog);
+
+       /* Header Style */
+       widget = gtk_check_button_new_with_mnemonic (_("_Header Style"));
+       dialog->priv->header_style_check = widget;
+
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_cell_dialog_set_header_style), dialog);
+
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+       gtk_box_pack_start (GTK_BOX (widget), dialog->priv->wrap_text_check, FALSE, FALSE, 0);
+       gtk_box_pack_start (GTK_BOX (widget), dialog->priv->header_style_check, FALSE, FALSE, 0);
+       gtk_grid_attach (grid, widget, 0, 1, 4, 1);
+
+       /* == Layout == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Layout</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 4, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 5, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Width */
+       widget = gtk_check_button_new_with_mnemonic (_("_Width"));
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+       dialog->priv->width_check = widget;
+
+       widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       dialog->priv->width_edit = widget;
+
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_cell_dialog_set_width), dialog);
+       g_object_bind_property (
+               dialog->priv->width_check, "active",
+               widget, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       widget = gtk_combo_box_text_new ();
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "unit-px", "px");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "unit-percent", "%");
+       gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+       dialog->priv->width_units = widget;
+
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_cell_dialog_set_width), dialog);
+       g_object_bind_property (
+               dialog->priv->width_check, "active",
+               widget, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       /* Row Span */
+       widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+       gtk_grid_attach (grid, widget, 4, 0, 1, 1);
+       dialog->priv->row_span_edit = widget;
+
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_cell_dialog_set_row_span), dialog);
+
+       widget = gtk_label_new_with_mnemonic (_("Row S_pan:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->row_span_edit);
+       gtk_grid_attach (grid, widget, 3, 0, 1, 1);
+
+       /* Column Span */
+       widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+       gtk_grid_attach (grid, widget, 4, 1, 1, 1);
+       dialog->priv->col_span_edit = widget;
+
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_cell_dialog_set_column_span), dialog);
+
+       widget = gtk_label_new_with_mnemonic (_("Co_lumn Span:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->col_span_edit);
+       gtk_grid_attach (grid, widget, 3, 1, 1, 1);
+
+       /* == Background == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Background</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 6, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 7, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Color */
+       widget = e_color_combo_new ();
+       e_color_combo_set_default_color (E_COLOR_COMBO (widget), &white);
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "notify::current-color",
+               G_CALLBACK (html_editor_cell_dialog_set_background_color), dialog);
+       dialog->priv->background_color_picker = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("C_olor:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (
+               GTK_LABEL (widget), dialog->priv->background_color_picker);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       /* Image */
+       widget = e_image_chooser_dialog_new (
+                       _("Choose Background Image"),
+                       GTK_WINDOW (dialog));
+       dialog->priv->background_image_chooser = widget;
+
+       file_filter = gtk_file_filter_new ();
+       gtk_file_filter_set_name (file_filter, _("Images"));
+       gtk_file_filter_add_mime_type (file_filter, "image/*");
+
+       widget = gtk_file_chooser_button_new_with_dialog (
+                       dialog->priv->background_image_chooser);
+       gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), file_filter);
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+       g_signal_connect_swapped (
+               widget, "file-set",
+               G_CALLBACK (html_editor_cell_dialog_set_background_image), dialog);
+       dialog->priv->background_image_chooser = widget;
+
+       widget =gtk_label_new_with_mnemonic (_("_Image:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (
+               GTK_LABEL (widget), dialog->priv->background_image_chooser);
+       gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_cell_dialog_new (EHTMLEditor *editor)
+{
+       return GTK_WIDGET (
+               g_object_new (
+                       E_TYPE_HTML_EDITOR_CELL_DIALOG,
+                       "editor", editor,
+                       "title", N_("Cell Properties"),
+                       NULL));
+}
+
+void
+e_html_editor_cell_dialog_show (EHTMLEditorCellDialog *dialog,
+                                WebKitDOMNode *cell)
+{
+       EHTMLEditorCellDialogClass *class;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_CELL_DIALOG (dialog));
+       g_return_if_fail (cell != NULL);
+
+       dialog->priv->cell = e_html_editor_dom_node_find_parent_element (cell, "TD");
+       if (dialog->priv->cell == NULL) {
+               dialog->priv->cell =
+                       e_html_editor_dom_node_find_parent_element (cell, "TH");
+       }
+
+       class = E_HTML_EDITOR_CELL_DIALOG_GET_CLASS (dialog);
+       GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog));
+}
+
diff --git a/e-util/e-html-editor-cell-dialog.h b/e-util/e-html-editor-cell-dialog.h
new file mode 100644
index 0000000..3a7f608
--- /dev/null
+++ b/e-util/e-html-editor-cell-dialog.h
@@ -0,0 +1,72 @@
+/*
+ * e-html-editor-cell-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_CELL_DIALOG_H
+#define E_HTML_EDITOR_CELL_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_CELL_DIALOG \
+       (e_html_editor_cell_dialog_get_type ())
+#define E_HTML_EDITOR_CELL_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_CELL_DIALOG, EHTMLEditorCellDialog))
+#define E_HTML_EDITOR_CELL_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_CELL_DIALOG, EHTMLEditorCellDialogClass))
+#define E_IS_HTML_EDITOR_CELL_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_CELL_DIALOG))
+#define E_IS_HTML_EDITOR_CELL_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_CELL_DIALOG))
+#define E_HTML_EDITOR_CELL_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_CELL_DIALOG, EHTMLEditorCellDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorCellDialog EHTMLEditorCellDialog;
+typedef struct _EHTMLEditorCellDialogClass EHTMLEditorCellDialogClass;
+typedef struct _EHTMLEditorCellDialogPrivate EHTMLEditorCellDialogPrivate;
+
+struct _EHTMLEditorCellDialog {
+       EHTMLEditorDialog parent;
+       EHTMLEditorCellDialogPrivate *priv;
+};
+
+struct _EHTMLEditorCellDialogClass {
+       EHTMLEditorDialogClass parent_class;
+};
+
+GType          e_html_editor_cell_dialog_get_type
+                                               (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_cell_dialog_new   (EHTMLEditor *editor);
+void           e_html_editor_cell_dialog_show  (EHTMLEditorCellDialog *dialog,
+                                                WebKitDOMNode *cell);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_CELL_DIALOG_H */
diff --git a/e-util/e-html-editor-dialog.c b/e-util/e-html-editor-dialog.c
new file mode 100644
index 0000000..5b43ebd
--- /dev/null
+++ b/e-util/e-html-editor-dialog.c
@@ -0,0 +1,248 @@
+/*
+ * e-html-editor-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-dialog.h"
+
+#define E_HTML_EDITOR_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_DIALOG, EHTMLEditorDialogPrivate))
+
+struct _EHTMLEditorDialogPrivate {
+       EHTMLEditor *editor;
+
+       GtkBox *button_box;
+       GtkGrid *container;
+};
+
+enum {
+       PROP_0,
+       PROP_EDITOR,
+};
+
+G_DEFINE_ABSTRACT_TYPE (
+       EHTMLEditorDialog,
+       e_html_editor_dialog,
+       GTK_TYPE_WINDOW);
+
+static void
+html_editor_dialog_set_editor (EHTMLEditorDialog *dialog,
+                               EHTMLEditor *editor)
+{
+       dialog->priv->editor = g_object_ref (editor);
+
+       g_object_notify (G_OBJECT (dialog), "editor");
+}
+
+static void
+html_editor_dialog_get_property (GObject *object,
+                            guint property_id,
+                            GValue *value,
+                            GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_EDITOR:
+                       g_value_set_object (
+                               value,
+                       e_html_editor_dialog_get_editor (
+                               E_HTML_EDITOR_DIALOG (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_dialog_set_property (GObject *object,
+                            guint property_id,
+                            const GValue *value,
+                            GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_EDITOR:
+                       html_editor_dialog_set_editor (
+                               E_HTML_EDITOR_DIALOG (object),
+                               g_value_get_object (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_dialog_constructed (GObject *object)
+{
+       EHTMLEditorDialog *dialog = E_HTML_EDITOR_DIALOG (object);
+
+       /* Chain up to parent implementation first */
+       G_OBJECT_CLASS (e_html_editor_dialog_parent_class)->constructed (object);
+
+       gtk_window_set_transient_for (
+               GTK_WINDOW (dialog),
+               GTK_WINDOW (gtk_widget_get_toplevel (
+                               GTK_WIDGET (dialog->priv->editor))));
+}
+
+static void
+html_editor_dialog_show (GtkWidget *widget)
+{
+       EHTMLEditorDialogPrivate *priv;
+
+       priv = E_HTML_EDITOR_DIALOG_GET_PRIVATE (widget);
+
+       gtk_widget_show_all (GTK_WIDGET (priv->container));
+       gtk_widget_show_all (GTK_WIDGET (priv->button_box));
+
+       GTK_WIDGET_CLASS (e_html_editor_dialog_parent_class)->show (widget);
+}
+
+static void
+html_editor_dialog_dispose (GObject *object)
+{
+       EHTMLEditorDialogPrivate *priv;
+
+       priv = E_HTML_EDITOR_DIALOG_GET_PRIVATE (object);
+
+       g_clear_object (&priv->editor);
+
+       /* Chain up to parent's implementation */
+       G_OBJECT_CLASS (e_html_editor_dialog_parent_class)->dispose (object);
+}
+
+static void
+e_html_editor_dialog_class_init (EHTMLEditorDialogClass *class)
+{
+       GObjectClass *object_class;
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorDialogPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->get_property = html_editor_dialog_get_property;
+       object_class->set_property = html_editor_dialog_set_property;
+       object_class->dispose = html_editor_dialog_dispose;
+       object_class->constructed = html_editor_dialog_constructed;
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->show = html_editor_dialog_show;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_EDITOR,
+               g_param_spec_object (
+                       "editor",
+                       NULL,
+                       NULL,
+                       E_TYPE_HTML_EDITOR,
+                       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+}
+
+static gboolean
+key_press_event_cb (GtkWidget *widget,
+                    GdkEventKey *event,
+                    gpointer user_data)
+{
+       if (event->keyval == GDK_KEY_Escape) {
+               gtk_widget_hide (widget);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+e_html_editor_dialog_init (EHTMLEditorDialog *dialog)
+{
+       GtkBox *main_layout;
+       GtkGrid *grid;
+       GtkWidget *widget, *button_box;
+
+       dialog->priv = E_HTML_EDITOR_DIALOG_GET_PRIVATE (dialog);
+
+       main_layout = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 5));
+       gtk_container_add (GTK_CONTAINER (dialog), GTK_WIDGET (main_layout));
+       gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 10);
+       gtk_grid_set_column_spacing (grid, 10);
+       gtk_box_pack_start (main_layout, GTK_WIDGET (grid), TRUE, TRUE, 5);
+       dialog->priv->container = grid;
+
+       /* == Button box == */
+       widget = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (gtk_widget_hide), dialog);
+
+       button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+       gtk_button_box_set_layout (GTK_BUTTON_BOX (button_box), GTK_BUTTONBOX_END);
+       gtk_box_set_spacing (GTK_BOX (button_box), 5);
+       gtk_box_pack_start (main_layout, button_box, TRUE, TRUE, 5);
+       gtk_box_pack_start (GTK_BOX (button_box), widget, FALSE, FALSE, 5);
+       dialog->priv->button_box = GTK_BOX (button_box);
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+
+       g_object_set (
+               G_OBJECT (dialog),
+               "destroy-with-parent", TRUE,
+               "modal", TRUE,
+               "resizable", FALSE,
+               "window-position", GTK_WIN_POS_CENTER_ON_PARENT,
+               NULL);
+
+       /* Don't destroy the dialog when closed! */
+       g_signal_connect (
+               dialog, "delete-event",
+               G_CALLBACK (gtk_widget_hide_on_delete), NULL);
+
+       g_signal_connect (
+               dialog, "key-press-event",
+               G_CALLBACK (key_press_event_cb), NULL);
+}
+
+EHTMLEditor *
+e_html_editor_dialog_get_editor (EHTMLEditorDialog *dialog)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_DIALOG (dialog), NULL);
+
+       return dialog->priv->editor;
+}
+
+GtkGrid *
+e_html_editor_dialog_get_container (EHTMLEditorDialog *dialog)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_DIALOG (dialog), NULL);
+
+       return dialog->priv->container;
+}
+
+GtkBox *
+e_html_editor_dialog_get_button_box (EHTMLEditorDialog *dialog)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_DIALOG (dialog), NULL);
+
+       return dialog->priv->button_box;
+}
+
diff --git a/e-util/e-html-editor-dialog.h b/e-util/e-html-editor-dialog.h
new file mode 100644
index 0000000..37fc7a5
--- /dev/null
+++ b/e-util/e-html-editor-dialog.h
@@ -0,0 +1,74 @@
+/*
+ * e-html-editor-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_DIALOG_H
+#define E_HTML_EDITOR_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-html-editor.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_DIALOG \
+       (e_html_editor_dialog_get_type ())
+#define E_HTML_EDITOR_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_DIALOG, EHTMLEditorDialog))
+#define E_HTML_EDITOR_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_DIALOG, EHTMLEditorDialogClass))
+#define E_IS_HTML_EDITOR_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_DIALOG))
+#define E_IS_HTML_EDITOR_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_DIALOG))
+#define E_HTML_EDITOR_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_DIALOG, EHTMLEditorDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorDialog EHTMLEditorDialog;
+typedef struct _EHTMLEditorDialogClass EHTMLEditorDialogClass;
+typedef struct _EHTMLEditorDialogPrivate EHTMLEditorDialogPrivate;
+
+struct _EHTMLEditorDialog {
+       GtkWindow parent;
+       EHTMLEditorDialogPrivate *priv;
+};
+
+struct _EHTMLEditorDialogClass {
+       GtkWindowClass parent_class;
+};
+
+GType          e_html_editor_dialog_get_type   (void) G_GNUC_CONST;
+EHTMLEditor *  e_html_editor_dialog_get_editor (EHTMLEditorDialog *dialog);
+GtkBox *       e_html_editor_dialog_get_button_box
+                                               (EHTMLEditorDialog *dialog);
+GtkGrid *      e_html_editor_dialog_get_container
+                                               (EHTMLEditorDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_DIALOG_H */
diff --git a/e-util/e-html-editor-find-dialog.c b/e-util/e-html-editor-find-dialog.c
new file mode 100644
index 0000000..9f44cf8
--- /dev/null
+++ b/e-util/e-html-editor-find-dialog.c
@@ -0,0 +1,224 @@
+/*
+ * e-html-editor-find-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-find-dialog.h"
+
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+
+#define E_HTML_EDITOR_FIND_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_FIND_DIALOG, EHTMLEditorFindDialogPrivate))
+
+struct _EHTMLEditorFindDialogPrivate {
+       GtkWidget *entry;
+       GtkWidget *backwards;
+       GtkWidget *case_sensitive;
+       GtkWidget *wrap_search;
+
+       GtkWidget *find_button;
+
+       GtkWidget *result_label;
+};
+
+G_DEFINE_TYPE (
+       EHTMLEditorFindDialog,
+       e_html_editor_find_dialog,
+       E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+reset_dialog (EHTMLEditorFindDialog *dialog)
+{
+       gtk_widget_set_sensitive (dialog->priv->find_button, TRUE);
+       gtk_widget_hide (dialog->priv->result_label);
+}
+
+static void
+html_editor_find_dialog_show (GtkWidget *widget)
+{
+       EHTMLEditorFindDialog *dialog = E_HTML_EDITOR_FIND_DIALOG (widget);
+
+       reset_dialog (dialog);
+       gtk_widget_grab_focus (dialog->priv->entry);
+
+       /* Chain up to parent's implementation */
+       GTK_WIDGET_CLASS (e_html_editor_find_dialog_parent_class)->show (widget);
+}
+
+static void
+html_editor_find_dialog_find_cb (EHTMLEditorFindDialog *dialog)
+{
+       gboolean found;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       found = webkit_web_view_search_text (
+                       WEBKIT_WEB_VIEW (view),
+                       gtk_entry_get_text (
+                               GTK_ENTRY (dialog->priv->entry)),
+                       gtk_toggle_button_get_active (
+                               GTK_TOGGLE_BUTTON (
+                                       dialog->priv->case_sensitive)),
+                       !gtk_toggle_button_get_active (
+                               GTK_TOGGLE_BUTTON (
+                                       dialog->priv->backwards)),
+                       gtk_toggle_button_get_active (
+                               GTK_TOGGLE_BUTTON (
+                                       dialog->priv->wrap_search)));
+
+       gtk_widget_set_sensitive (dialog->priv->find_button, found);
+
+       /* We give focus to WebKit so that the selection is highlited.
+        * Without focus selection is not visible (at least with my default
+        * color scheme). The focus in fact is not given to WebKit, because
+        * this dialog is modal, but it satisfies it in a way that it paints
+        * the selection :) */
+       gtk_widget_grab_focus (GTK_WIDGET (view));
+
+       if (!found) {
+               gtk_label_set_label (
+                       GTK_LABEL (dialog->priv->result_label),
+                       N_("No match found"));
+               gtk_widget_show (dialog->priv->result_label);
+       }
+}
+
+static gboolean
+entry_key_release_event (GtkWidget *widget,
+                         GdkEvent *event,
+                         gpointer user_data)
+{
+       GdkEventKey *key = &event->key;
+       EHTMLEditorFindDialog *dialog = user_data;
+
+       if (key->keyval == GDK_KEY_Return) {
+               html_editor_find_dialog_find_cb (dialog);
+               return TRUE;
+       }
+
+       reset_dialog (dialog);
+       return FALSE;
+}
+
+static void
+e_html_editor_find_dialog_class_init (EHTMLEditorFindDialogClass *class)
+{
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorFindDialogPrivate));
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->show = html_editor_find_dialog_show;
+}
+
+static void
+e_html_editor_find_dialog_init (EHTMLEditorFindDialog *dialog)
+{
+       GtkGrid *main_layout;
+       GtkBox *box;
+       GtkWidget *widget;
+
+       dialog->priv = E_HTML_EDITOR_FIND_DIALOG_GET_PRIVATE (dialog);
+
+       main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+       widget = gtk_entry_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+       dialog->priv->entry = widget;
+       g_signal_connect (
+               widget, "key-release-event",
+               G_CALLBACK (entry_key_release_event), dialog);
+
+       box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5));
+       gtk_grid_attach (main_layout, GTK_WIDGET (box), 0, 1, 1, 1);
+
+       widget = gtk_check_button_new_with_mnemonic (N_("Search _backwards"));
+       gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
+       dialog->priv->backwards = widget;
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (reset_dialog), dialog);
+
+       widget = gtk_check_button_new_with_mnemonic (N_("Case _Sensitive"));
+       gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
+       dialog->priv->case_sensitive = widget;
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (reset_dialog), dialog);
+
+       widget = gtk_check_button_new_with_mnemonic (N_("_Wrap Search"));
+       gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
+       dialog->priv->wrap_search = widget;
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (reset_dialog), dialog);
+
+       box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5));
+       gtk_grid_attach (main_layout, GTK_WIDGET (box), 0, 2, 1, 1);
+
+       widget = gtk_label_new ("");
+       gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
+       dialog->priv->result_label = widget;
+
+       widget = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+       gtk_box_set_spacing (GTK_BOX (widget), 5);
+       gtk_button_box_set_layout (GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_END);
+       gtk_box_pack_end (box, widget, TRUE, TRUE, 0);
+       box = GTK_BOX (widget);
+
+       box = e_html_editor_dialog_get_button_box (E_HTML_EDITOR_DIALOG (dialog));
+       widget = gtk_button_new_from_stock (GTK_STOCK_FIND);
+       gtk_box_pack_start (box, widget, FALSE, FALSE, 5);
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_find_dialog_find_cb), dialog);
+       dialog->priv->find_button = widget;
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_find_dialog_new (EHTMLEditor *editor)
+{
+       return GTK_WIDGET (
+               g_object_new (
+                       E_TYPE_HTML_EDITOR_FIND_DIALOG,
+                       "editor", editor,
+                       "icon-name", GTK_STOCK_FIND,
+                       "title", N_("Find"),
+                       NULL));
+}
+
+void
+e_html_editor_find_dialog_find_next (EHTMLEditorFindDialog *dialog)
+{
+       if (gtk_entry_get_text_length (GTK_ENTRY (dialog->priv->entry)) == 0) {
+               return;
+       }
+
+       html_editor_find_dialog_find_cb (dialog);
+}
diff --git a/e-util/e-html-editor-find-dialog.h b/e-util/e-html-editor-find-dialog.h
new file mode 100644
index 0000000..60134f4
--- /dev/null
+++ b/e-util/e-html-editor-find-dialog.h
@@ -0,0 +1,73 @@
+/*
+ * e-html-editor-find-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_FIND_DIALOG_H
+#define E_HTML_EDITOR_FIND_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_FIND_DIALOG \
+       (e_html_editor_find_dialog_get_type ())
+#define E_HTML_EDITOR_FIND_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_FIND_DIALOG, EHTMLEditorFindDialog))
+#define E_HTML_EDITOR_FIND_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_FIND_DIALOG, EHTMLEditorFindDialogClass))
+#define E_IS_HTML_EDITOR_FIND_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_FIND_DIALOG))
+#define E_IS_HTML_EDITOR_FIND_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_FIND_DIALOG))
+#define E_HTML_EDITOR_FIND_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_FIND_DIALOG, EHTMLEditorFindDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorFindDialog EHTMLEditorFindDialog;
+typedef struct _EHTMLEditorFindDialogClass EHTMLEditorFindDialogClass;
+typedef struct _EHTMLEditorFindDialogPrivate EHTMLEditorFindDialogPrivate;
+
+struct _EHTMLEditorFindDialog {
+       EHTMLEditorDialog parent;
+       EHTMLEditorFindDialogPrivate *priv;
+};
+
+struct _EHTMLEditorFindDialogClass {
+       EHTMLEditorDialogClass parent_class;
+};
+
+GType          e_html_editor_find_dialog_get_type
+                                               (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_find_dialog_new   (EHTMLEditor *editor);
+void           e_html_editor_find_dialog_find_next
+                                               (EHTMLEditorFindDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_FIND_DIALOG_H */
+
diff --git a/e-util/e-html-editor-hrule-dialog.c b/e-util/e-html-editor-hrule-dialog.c
new file mode 100644
index 0000000..9ace655
--- /dev/null
+++ b/e-util/e-html-editor-hrule-dialog.c
@@ -0,0 +1,421 @@
+/*
+ * e-html-editor-hrule-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-hrule-dialog.h"
+#include "e-html-editor-utils.h"
+#include "e-html-editor-view.h"
+
+#include <glib/gi18n-lib.h>
+#include <webkit/webkitdom.h>
+#include <stdlib.h>
+
+#define E_HTML_EDITOR_HRULE_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_HRULE_DIALOG, EHTMLEditorHRuleDialogPrivate))
+
+struct _EHTMLEditorHRuleDialogPrivate {
+       GtkWidget *width_edit;
+       GtkWidget *size_edit;
+       GtkWidget *unit_combo;
+
+       GtkWidget *alignment_combo;
+       GtkWidget *shaded_check;
+
+       WebKitDOMHTMLHRElement *hr_element;
+};
+
+G_DEFINE_TYPE (
+       EHTMLEditorHRuleDialog,
+       e_html_editor_hrule_dialog,
+       E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+html_editor_hrule_dialog_set_alignment (EHTMLEditorHRuleDialog *dialog)
+{
+       const gchar *alignment;
+
+       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+       alignment = gtk_combo_box_get_active_id (
+                       GTK_COMBO_BOX (dialog->priv->alignment_combo));
+
+       webkit_dom_htmlhr_element_set_align (dialog->priv->hr_element, alignment);
+}
+
+static void
+html_editor_hrule_dialog_get_alignment (EHTMLEditorHRuleDialog *dialog)
+{
+       gchar *alignment;
+
+       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+       alignment = webkit_dom_htmlhr_element_get_align (dialog->priv->hr_element);
+
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->alignment_combo), alignment);
+       g_free (alignment);
+}
+
+static void
+html_editor_hrule_dialog_set_size (EHTMLEditorHRuleDialog *dialog)
+{
+       gchar *size;
+
+       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+       size = g_strdup_printf (
+               "%d",
+               (gint) gtk_spin_button_get_value (
+                       GTK_SPIN_BUTTON (dialog->priv->size_edit)));
+
+       webkit_dom_htmlhr_element_set_size (dialog->priv->hr_element, size);
+
+       g_free (size);
+}
+
+static void
+html_editor_hrule_dialog_get_size (EHTMLEditorHRuleDialog *dialog)
+{
+       gchar *size;
+       gint size_int = 0;
+
+       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+       size = webkit_dom_htmlhr_element_get_size (dialog->priv->hr_element);
+       if (size && *size) {
+               size_int = atoi (size);
+       }
+
+       if (size_int == 0) {
+               size_int = 2;
+       }
+
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->size_edit), (gdouble) size_int);
+
+       g_free (size);
+}
+
+static void
+html_editor_hrule_dialog_set_width (EHTMLEditorHRuleDialog *dialog)
+{
+       gchar *width, *units;
+
+       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+       units = gtk_combo_box_text_get_active_text (
+                       GTK_COMBO_BOX_TEXT (dialog->priv->unit_combo));
+       width = g_strdup_printf (
+               "%d%s",
+               (gint) gtk_spin_button_get_value (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit)),
+               units);
+
+       webkit_dom_htmlhr_element_set_width (dialog->priv->hr_element, width);
+
+       g_free (units);
+       g_free (width);
+}
+
+static void
+html_editor_hrule_dialog_get_width (EHTMLEditorHRuleDialog *dialog)
+{
+       gchar *width;
+       const gchar *units;
+       gint width_int = 0;
+
+       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+       width = webkit_dom_htmlhr_element_get_width (dialog->priv->hr_element);
+       if (width && *width) {
+               width_int = atoi (width);
+
+               if (strstr (width, "%") != NULL) {
+                       units = "units-percent";
+               } else {
+                       units = "units-px";
+               }
+       }
+
+       if (width_int == 0) {
+               width_int = 100;
+               units = "units-percent";
+       }
+
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->width_edit), (gdouble) width_int);
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->unit_combo), units);
+
+       g_free (width);
+}
+
+static void
+html_editor_hrule_dialog_set_shading (EHTMLEditorHRuleDialog *dialog)
+{
+       gboolean no_shade;
+
+       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+       no_shade = !gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->shaded_check));
+
+       webkit_dom_htmlhr_element_set_no_shade (dialog->priv->hr_element, no_shade);
+}
+
+static void
+html_editor_hrule_dialog_get_shading (EHTMLEditorHRuleDialog *dialog)
+{
+       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+
+       gtk_toggle_button_set_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->shaded_check),
+               !webkit_dom_htmlhr_element_get_no_shade (dialog->priv->hr_element));
+}
+
+static void
+html_editor_hrule_dialog_hide (GtkWidget *widget)
+{
+       EHTMLEditorHRuleDialogPrivate *priv;
+
+       priv = E_HTML_EDITOR_HRULE_DIALOG_GET_PRIVATE (widget);
+
+       priv->hr_element = NULL;
+
+       GTK_WIDGET_CLASS (e_html_editor_hrule_dialog_parent_class)->hide (widget);
+}
+
+static void
+html_editor_hrule_dialog_show (GtkWidget *widget)
+{
+       EHTMLEditorHRuleDialog *dialog;
+       EHTMLEditor *editor;
+       EHTMLEditorSelection *editor_selection;
+       EHTMLEditorView *view;
+
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *selection;
+       WebKitDOMElement *rule;
+
+       dialog = E_HTML_EDITOR_HRULE_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       editor_selection = e_html_editor_view_get_selection (view);
+
+       document = webkit_web_view_get_dom_document (
+                       WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       selection = webkit_dom_dom_window_get_selection (window);
+       if (webkit_dom_dom_selection_get_range_count (selection) < 1) {
+               GTK_WIDGET_CLASS (e_html_editor_hrule_dialog_parent_class)->show (widget);
+               return;
+       }
+
+       rule = e_html_editor_view_get_element_under_mouse_click (view);
+       if (!rule) {
+               WebKitDOMElement *caret, *parent, *element;
+
+               caret = e_html_editor_selection_save_caret_position (editor_selection);
+
+               parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (caret));
+               element = caret;
+
+               while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+                       element = parent;
+                       parent = webkit_dom_node_get_parent_element (
+                               WEBKIT_DOM_NODE (parent));
+               }
+
+               rule = webkit_dom_document_create_element (document, "HR", NULL);
+
+               /* Insert horizontal rule into body below the caret */
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (parent),
+                       WEBKIT_DOM_NODE (rule),
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
+                       NULL);
+
+               e_html_editor_selection_clear_caret_position_marker (editor_selection);
+
+               dialog->priv->hr_element = WEBKIT_DOM_HTMLHR_ELEMENT (rule);
+
+               /* For new rule reset the values to default */
+               gtk_spin_button_set_value (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit), 100.0);
+               gtk_combo_box_set_active_id (
+                       GTK_COMBO_BOX (dialog->priv->unit_combo), "units-percent");
+               gtk_spin_button_set_value (
+                       GTK_SPIN_BUTTON (dialog->priv->size_edit), 2.0);
+               gtk_combo_box_set_active_id (
+                       GTK_COMBO_BOX (dialog->priv->alignment_combo), "left");
+               gtk_toggle_button_set_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->shaded_check), FALSE);
+
+               html_editor_hrule_dialog_set_alignment (dialog);
+               html_editor_hrule_dialog_set_size (dialog);
+               html_editor_hrule_dialog_set_alignment (dialog);
+               html_editor_hrule_dialog_set_shading (dialog);
+
+               e_html_editor_view_set_changed (view, TRUE);
+       } else {
+               dialog->priv->hr_element = WEBKIT_DOM_HTMLHR_ELEMENT (rule);
+
+               html_editor_hrule_dialog_get_alignment (dialog);
+               html_editor_hrule_dialog_get_size (dialog);
+               html_editor_hrule_dialog_get_width (dialog);
+               html_editor_hrule_dialog_get_shading (dialog);
+       }
+
+       /* Chain up to parent implementation */
+       GTK_WIDGET_CLASS (e_html_editor_hrule_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_hrule_dialog_class_init (EHTMLEditorHRuleDialogClass *class)
+{
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorHRuleDialogPrivate));
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->show = html_editor_hrule_dialog_show;
+       widget_class->hide = html_editor_hrule_dialog_hide;
+}
+
+static void
+e_html_editor_hrule_dialog_init (EHTMLEditorHRuleDialog *dialog)
+{
+       GtkGrid *main_layout, *grid;
+       GtkWidget *widget;
+
+       dialog->priv = E_HTML_EDITOR_HRULE_DIALOG_GET_PRIVATE (dialog);
+
+       main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+       /* == Size == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Size</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+
+       /* Width */
+       widget = gtk_spin_button_new_with_range (0.0, 100.0, 1.0);
+       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), 100);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_hrule_dialog_set_width), dialog);
+       dialog->priv->width_edit = widget;
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+
+       widget = gtk_label_new_with_mnemonic (_("_Width:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->size_edit);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       widget = gtk_combo_box_text_new ();
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%");
+       gtk_combo_box_set_active_id (GTK_COMBO_BOX (widget), "units-percent");
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_hrule_dialog_set_width), dialog);
+       dialog->priv->unit_combo = widget;
+       gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+
+       /* Size */
+       widget = gtk_spin_button_new_with_range (0.0, 100.0, 1.0);
+       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), 2);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_hrule_dialog_set_size), dialog);
+       dialog->priv->size_edit = widget;
+       gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+
+       widget = gtk_label_new_with_mnemonic (_("_Size:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->size_edit);
+       gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+       /* == Style == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Style</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+
+       /* Alignment */
+       widget = gtk_combo_box_text_new ();
+       gtk_combo_box_text_append (
+               GTK_COMBO_BOX_TEXT (widget), "left", _("Left"));
+       gtk_combo_box_text_append (
+               GTK_COMBO_BOX_TEXT (widget), "center", _("Center"));
+       gtk_combo_box_text_append (
+               GTK_COMBO_BOX_TEXT (widget), "right", _("Right"));
+       gtk_combo_box_set_active_id (GTK_COMBO_BOX (widget), "left");
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_hrule_dialog_set_alignment), dialog);
+       dialog->priv->alignment_combo = widget;
+       gtk_grid_attach (grid, widget, 1, 0, 2, 1);
+
+       widget = gtk_label_new_with_mnemonic (_("_Alignment:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), widget);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       /* Shaded */
+       widget = gtk_check_button_new_with_mnemonic (_("S_haded"));
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_hrule_dialog_set_shading), dialog);
+       dialog->priv->shaded_check = widget;
+       gtk_grid_attach (grid, widget, 0, 1, 2, 1);
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_hrule_dialog_new (EHTMLEditor *editor)
+{
+       return GTK_WIDGET (
+               g_object_new (
+                       E_TYPE_HTML_EDITOR_HRULE_DIALOG,
+                       "editor", editor,
+                       "title", _("Rule properties"),
+                       NULL));
+}
diff --git a/e-util/e-html-editor-hrule-dialog.h b/e-util/e-html-editor-hrule-dialog.h
new file mode 100644
index 0000000..876fc25
--- /dev/null
+++ b/e-util/e-html-editor-hrule-dialog.h
@@ -0,0 +1,70 @@
+/*
+ * e-html-editor-hrule-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_HRULE_DIALOG_H
+#define E_HTML_EDITOR_HRULE_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_HRULE_DIALOG \
+       (e_html_editor_hrule_dialog_get_type ())
+#define E_HTML_EDITOR_HRULE_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_HRULE_DIALOG, EHTMLEditorHRuleDialog))
+#define E_HTML_EDITOR_HRULE_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_HRULE_DIALOG, EHTMLEditorHRuleDialogClass))
+#define E_IS_HTML_EDITOR_HRULE_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_HRULE_DIALOG))
+#define E_IS_HTML_EDITOR_HRULE_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_HRULE_DIALOG))
+#define E_HTML_EDITOR_HRULE_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_HRULE_DIALOG, EHTMLEditorHRuleDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorHRuleDialog EHTMLEditorHRuleDialog;
+typedef struct _EHTMLEditorHRuleDialogClass EHTMLEditorHRuleDialogClass;
+typedef struct _EHTMLEditorHRuleDialogPrivate EHTMLEditorHRuleDialogPrivate;
+
+struct _EHTMLEditorHRuleDialog {
+       EHTMLEditorDialog parent;
+       EHTMLEditorHRuleDialogPrivate *priv;
+};
+
+struct _EHTMLEditorHRuleDialogClass {
+       EHTMLEditorDialogClass parent_class;
+};
+
+GType          e_html_editor_hrule_dialog_get_type
+                                               (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_hrule_dialog_new  (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_HRULE_DIALOG_H */
diff --git a/e-util/e-html-editor-image-dialog.c b/e-util/e-html-editor-image-dialog.c
new file mode 100644
index 0000000..346de44
--- /dev/null
+++ b/e-util/e-html-editor-image-dialog.c
@@ -0,0 +1,703 @@
+/*
+ * e-html-editor-image-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-image-dialog.h"
+
+#include <stdlib.h>
+#include <glib/gi18n-lib.h>
+
+#include "e-html-editor-utils.h"
+#include "e-image-chooser-dialog.h"
+
+#define E_HTML_EDITOR_IMAGE_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_IMAGE_DIALOG, EHTMLEditorImageDialogPrivate))
+
+struct _EHTMLEditorImageDialogPrivate {
+       GtkWidget *file_chooser;
+       GtkWidget *description_edit;
+
+       GtkWidget *width_edit;
+       GtkWidget *width_units;
+       GtkWidget *height_edit;
+       GtkWidget *height_units;
+       GtkWidget *alignment;
+
+       GtkWidget *x_padding_edit;
+       GtkWidget *y_padding_edit;
+       GtkWidget *border_edit;
+
+       GtkWidget *url_edit;
+       GtkWidget *test_url_button;
+
+       WebKitDOMHTMLImageElement *image;
+};
+
+G_DEFINE_TYPE (
+       EHTMLEditorImageDialog,
+       e_html_editor_image_dialog,
+       E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+html_editor_image_dialog_set_src (EHTMLEditorImageDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorSelection *editor_selection;
+       EHTMLEditorView *view;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       editor_selection = e_html_editor_view_get_selection (view);
+
+       e_html_editor_selection_replace_image_src (
+               editor_selection,
+               WEBKIT_DOM_ELEMENT (dialog->priv->image),
+               gtk_file_chooser_get_uri (
+                       GTK_FILE_CHOOSER (dialog->priv->file_chooser)));
+}
+
+static void
+html_editor_image_dialog_set_alt (EHTMLEditorImageDialog *dialog)
+{
+       webkit_dom_html_image_element_set_alt (
+               dialog->priv->image,
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->description_edit)));
+}
+
+static void
+html_editor_image_dialog_set_width (EHTMLEditorImageDialog *dialog)
+{
+       gint requested;
+       gulong natural;
+       gint width;
+
+       natural = webkit_dom_html_image_element_get_natural_width (
+                       dialog->priv->image);
+       requested = gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit));
+
+       switch (gtk_combo_box_get_active (
+               GTK_COMBO_BOX (dialog->priv->width_units))) {
+
+               case 0: /* px */
+                       width = requested;
+                       break;
+
+               case 1: /* percent */
+                       width = natural * requested * 0.01;
+                       break;
+
+               case 2: /* follow */
+                       width = natural;
+                       break;
+
+       }
+
+       webkit_dom_html_image_element_set_width (dialog->priv->image, width);
+}
+
+static void
+html_editor_image_dialog_set_width_units (EHTMLEditorImageDialog *dialog)
+{
+       gint requested;
+       gulong natural;
+       gint width = 0;
+
+       natural = webkit_dom_html_image_element_get_natural_width (
+                       dialog->priv->image);
+       requested = gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit));
+
+       switch (gtk_combo_box_get_active (
+               GTK_COMBO_BOX (dialog->priv->width_units))) {
+
+               case 0: /* px */
+                       if (gtk_widget_is_sensitive (dialog->priv->width_edit)) {
+                               width = requested * natural * 0.01;
+                       } else {
+                               width = natural;
+                       }
+                       webkit_dom_element_remove_attribute (
+                               WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
+                       gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE);
+                       break;
+
+               case 1: /* percent */
+                       if (gtk_widget_is_sensitive (dialog->priv->width_edit)) {
+                               width = (((gdouble) requested) / natural) * 100;
+                       } else {
+                               width = 100;
+                       }
+                       webkit_dom_element_remove_attribute (
+                               WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
+                       gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE);
+                       break;
+
+               case 2: /* follow */
+                       webkit_dom_element_set_attribute (
+                               WEBKIT_DOM_ELEMENT (dialog->priv->image),
+                               "style",
+                               "width: auto;",
+                               NULL);
+                       gtk_widget_set_sensitive (dialog->priv->width_edit, FALSE);
+                       break;
+       }
+
+       if (width != 0) {
+               gtk_spin_button_set_value (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit), width);
+       }
+}
+
+static void
+html_editor_image_dialog_set_height (EHTMLEditorImageDialog *dialog)
+{
+       gint requested;
+       gulong natural;
+       gint height;
+
+       natural = webkit_dom_html_image_element_get_natural_height (
+                       dialog->priv->image);
+       requested = gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->height_edit));
+
+       switch (gtk_combo_box_get_active (
+               GTK_COMBO_BOX (dialog->priv->height_units))) {
+
+               case 0: /* px */
+                       height = requested;
+                       break;
+
+               case 1: /* percent */
+                       height = natural * requested * 0.01;
+                       break;
+
+               case 2: /* follow */
+                       height = natural;
+                       break;
+
+       }
+
+       webkit_dom_html_image_element_set_height (dialog->priv->image, height);
+}
+
+static void
+html_editor_image_dialog_set_height_units (EHTMLEditorImageDialog *dialog)
+{
+       gint requested;
+       gulong natural;
+       gint height = -1;
+
+       natural = webkit_dom_html_image_element_get_natural_height (
+                       dialog->priv->image);
+       requested = gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->height_edit));
+
+       switch (gtk_combo_box_get_active (
+               GTK_COMBO_BOX (dialog->priv->height_units))) {
+
+               case 0: /* px */
+                       if (gtk_widget_is_sensitive (dialog->priv->height_edit)) {
+                               height = requested * natural * 0.01;
+                       } else {
+                               height = natural;
+                       }
+                       webkit_dom_element_remove_attribute (
+                               WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
+                       gtk_widget_set_sensitive (dialog->priv->height_edit, TRUE);
+                       break;
+
+               case 1: /* percent */
+                       if (gtk_widget_is_sensitive (dialog->priv->height_edit)) {
+                               height = (((gdouble) requested) / natural) * 100;
+                       } else {
+                               height = 100;
+                       }
+                       webkit_dom_element_remove_attribute (
+                               WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
+                       gtk_widget_set_sensitive (dialog->priv->height_edit, TRUE);
+                       break;
+
+               case 2: /* follow */
+                       webkit_dom_element_set_attribute (
+                               WEBKIT_DOM_ELEMENT (dialog->priv->image),
+                               "style",
+                               "height: auto;",
+                               NULL);
+                       gtk_widget_set_sensitive (dialog->priv->height_edit, FALSE);
+                       break;
+       }
+
+       if (height != -1) {
+               gtk_spin_button_set_value (
+                       GTK_SPIN_BUTTON (dialog->priv->height_edit), height);
+       }
+}
+
+static void
+html_editor_image_dialog_set_alignment (EHTMLEditorImageDialog *dialog)
+{
+       webkit_dom_html_image_element_set_align (
+               dialog->priv->image,
+               gtk_combo_box_get_active_id (
+                       GTK_COMBO_BOX (dialog->priv->alignment)));
+}
+
+static void
+html_editor_image_dialog_set_x_padding (EHTMLEditorImageDialog *dialog)
+{
+       webkit_dom_html_image_element_set_hspace (
+               dialog->priv->image,
+               gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->x_padding_edit)));
+}
+
+static void
+html_editor_image_dialog_set_y_padding (EHTMLEditorImageDialog *dialog)
+{
+       webkit_dom_html_image_element_set_vspace (
+               dialog->priv->image,
+               gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->y_padding_edit)));
+}
+
+static void
+html_editor_image_dialog_set_border (EHTMLEditorImageDialog *dialog)
+{
+       gchar *val;
+
+       val = g_strdup_printf (
+               "%d", gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->border_edit)));
+
+       webkit_dom_html_image_element_set_border (dialog->priv->image, val);
+
+       g_free (val);
+}
+
+static void
+html_editor_image_dialog_set_url (EHTMLEditorImageDialog *dialog)
+{
+       WebKitDOMElement *link;
+       const gchar *url;
+
+       url = gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit));
+       link = e_html_editor_dom_node_find_parent_element (
+               WEBKIT_DOM_NODE (dialog->priv->image), "A");
+
+       if (link) {
+               if (!url || !*url) {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (link)),
+                               WEBKIT_DOM_NODE (dialog->priv->image),
+                               WEBKIT_DOM_NODE (link), NULL);
+                       webkit_dom_node_remove_child (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (link)),
+                               WEBKIT_DOM_NODE (link), NULL);
+               } else {
+                       webkit_dom_html_anchor_element_set_href (
+                               WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url);
+               }
+       } else {
+               if (url && *url) {
+                       WebKitDOMDocument *document;
+
+                       document = webkit_dom_node_get_owner_document (
+                                       WEBKIT_DOM_NODE (dialog->priv->image));
+                       link = webkit_dom_document_create_element (
+                                       document, "A", NULL);
+
+                       webkit_dom_html_anchor_element_set_href (
+                               WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url);
+
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (dialog->priv->image)),
+                               WEBKIT_DOM_NODE (link),
+                               WEBKIT_DOM_NODE (dialog->priv->image), NULL);
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (link),
+                               WEBKIT_DOM_NODE (dialog->priv->image), NULL);
+               }
+       }
+}
+
+static void
+html_editor_image_dialog_test_url (EHTMLEditorImageDialog *dialog)
+{
+       gtk_show_uri (
+               gtk_window_get_screen (GTK_WINDOW (dialog)),
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)),
+               GDK_CURRENT_TIME,
+               NULL);
+}
+
+static void
+html_editor_image_dialog_show (GtkWidget *widget)
+{
+       EHTMLEditorImageDialog *dialog;
+       WebKitDOMElement *link;
+       gchar *tmp;
+       glong val;
+
+       dialog = E_HTML_EDITOR_IMAGE_DIALOG (widget);
+
+       if (!dialog->priv->image) {
+               return;
+       }
+
+       tmp = webkit_dom_element_get_attribute (
+               WEBKIT_DOM_ELEMENT (dialog->priv->image), "data-uri");
+       if (tmp && *tmp) {
+               gtk_file_chooser_set_uri (
+                       GTK_FILE_CHOOSER (dialog->priv->file_chooser), tmp);
+               gtk_widget_set_sensitive (
+                       GTK_WIDGET (dialog->priv->file_chooser), TRUE);
+               g_free (tmp);
+       } else {
+               gtk_file_chooser_set_uri (
+                       GTK_FILE_CHOOSER (dialog->priv->file_chooser), "");
+               gtk_widget_set_sensitive (
+                       GTK_WIDGET (dialog->priv->file_chooser), FALSE);
+       }
+
+       tmp = webkit_dom_html_image_element_get_alt (dialog->priv->image);
+       gtk_entry_set_text (GTK_ENTRY (dialog->priv->description_edit), tmp ? tmp : "");
+       g_free (tmp);
+
+       val = webkit_dom_html_image_element_get_width (dialog->priv->image);
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->width_edit), val);
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->width_units), "units-px");
+
+       val = webkit_dom_html_image_element_get_height (dialog->priv->image);
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->height_edit), val);
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->height_units), "units-px");
+
+       tmp = webkit_dom_html_image_element_get_border (dialog->priv->image);
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->alignment),
+               (tmp && *tmp) ? tmp : "bottom");
+       g_free (tmp);
+
+       val = webkit_dom_html_image_element_get_hspace (dialog->priv->image);
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->x_padding_edit), val);
+
+       val = webkit_dom_html_image_element_get_vspace (dialog->priv->image);
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->y_padding_edit), val);
+
+       link = e_html_editor_dom_node_find_parent_element (
+                       WEBKIT_DOM_NODE (dialog->priv->image), "A");
+       if (link) {
+               tmp = webkit_dom_html_anchor_element_get_href (
+                               WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link));
+               gtk_entry_set_text (GTK_ENTRY (dialog->priv->url_edit), tmp);
+               g_free (tmp);
+       }
+
+       /* Chain up to parent implementation */
+       GTK_WIDGET_CLASS (e_html_editor_image_dialog_parent_class)->show (widget);
+}
+
+static void
+html_editor_image_dialog_hide (GtkWidget *widget)
+{
+       EHTMLEditorImageDialogPrivate *priv;
+
+       priv = E_HTML_EDITOR_IMAGE_DIALOG_GET_PRIVATE (widget);
+
+       priv->image = NULL;
+
+       GTK_WIDGET_CLASS (e_html_editor_image_dialog_parent_class)->hide (widget);
+}
+
+static void
+e_html_editor_image_dialog_class_init (EHTMLEditorImageDialogClass *class)
+{
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorImageDialogPrivate));
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->show = html_editor_image_dialog_show;
+       widget_class->hide = html_editor_image_dialog_hide;
+}
+
+static void
+e_html_editor_image_dialog_init (EHTMLEditorImageDialog *dialog)
+{
+       GtkGrid *main_layout, *grid;
+       GtkWidget *widget;
+       GtkFileFilter *file_filter;
+
+       dialog->priv = E_HTML_EDITOR_IMAGE_DIALOG_GET_PRIVATE (dialog);
+
+       main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+       /* == General == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>General</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Source */
+       widget = e_image_chooser_dialog_new (
+                       _("Choose Background Image"),
+                       GTK_WINDOW (dialog));
+       gtk_file_chooser_set_action (
+               GTK_FILE_CHOOSER (widget), GTK_FILE_CHOOSER_ACTION_OPEN);
+
+       file_filter = gtk_file_filter_new ();
+       gtk_file_filter_set_name (file_filter, _("Images"));
+       gtk_file_filter_add_mime_type (file_filter, "image/*");
+
+       widget = gtk_file_chooser_button_new_with_dialog (widget);
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "file-set",
+               G_CALLBACK (html_editor_image_dialog_set_src), dialog);
+       dialog->priv->file_chooser = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Source:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->file_chooser);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       /* Description */
+       widget = gtk_entry_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+       g_signal_connect_swapped (
+               widget, "notify::text",
+               G_CALLBACK (html_editor_image_dialog_set_alt), dialog);
+       dialog->priv->description_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Description:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->description_edit);
+       gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+       /* == Layout == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Layout</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Width */
+       widget = gtk_spin_button_new_with_range (1, G_MAXUINT, 1);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_image_dialog_set_width), dialog);
+       dialog->priv->width_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Width:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->width_edit);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       widget = gtk_combo_box_text_new ();
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-follow", "follow");
+       gtk_combo_box_set_active_id (GTK_COMBO_BOX (widget), "units-px");
+       gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_image_dialog_set_width_units), dialog);
+       dialog->priv->width_units = widget;
+
+       /* Height */
+       widget = gtk_spin_button_new_with_range (1, G_MAXUINT, 1);
+       gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_image_dialog_set_height), dialog);
+       dialog->priv->height_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Height:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->height_edit);
+       gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+       widget = gtk_combo_box_text_new ();
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-follow", "follow");
+       gtk_combo_box_set_active_id (GTK_COMBO_BOX (widget), "units-px");
+       gtk_grid_attach (grid, widget, 2, 1, 1, 1);
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_image_dialog_set_height_units), dialog);
+       dialog->priv->height_units = widget;
+
+       /* Alignment */
+       widget = gtk_combo_box_text_new ();
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "top", _("Top"));
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "middle", _("Middle"));
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "bottom", _("Bottom"));
+       gtk_combo_box_set_active_id (GTK_COMBO_BOX (widget), "bottom");
+       gtk_grid_attach (grid, widget, 1, 2, 1, 1);
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_image_dialog_set_alignment), dialog);
+       dialog->priv->alignment = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Alignment"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->alignment);
+       gtk_grid_attach (grid, widget, 0, 2, 1, 1);
+
+       /* X Padding */
+       widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+       gtk_grid_attach (grid, widget, 5, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_image_dialog_set_x_padding), dialog);
+       dialog->priv->x_padding_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_X-Padding:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->x_padding_edit);
+       gtk_grid_attach (grid, widget, 4, 0, 1, 1);
+
+       widget = gtk_label_new ("px");
+       gtk_grid_attach (grid, widget, 6, 0, 1, 1);
+
+       /* Y Padding */
+       widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+       gtk_grid_attach (grid, widget, 5, 1, 1, 1);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_image_dialog_set_y_padding), dialog);
+       dialog->priv->y_padding_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Y-Padding:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->y_padding_edit);
+       gtk_grid_attach (grid, widget, 4, 1, 1, 1);
+
+       widget = gtk_label_new ("px");
+       gtk_grid_attach (grid, widget, 6, 1, 1, 1);
+
+       /* Border */
+       widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+       gtk_grid_attach (grid, widget, 5, 2, 1, 1);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_image_dialog_set_border), dialog);
+       dialog->priv->border_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Border:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->border_edit);
+       gtk_grid_attach (grid, widget, 4, 2, 1, 1);
+
+       widget = gtk_label_new ("px");
+       gtk_grid_attach (grid, widget, 6, 2, 1, 1);
+
+       /* == Layout == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Link</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 4, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 5, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       widget = gtk_entry_new ();
+       gtk_grid_attach (grid, widget, 1 ,0, 1, 1);
+       gtk_widget_set_hexpand (widget, TRUE);
+       g_signal_connect_swapped (
+               widget, "notify::text",
+               G_CALLBACK (html_editor_image_dialog_set_url), dialog);
+       dialog->priv->url_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_URL:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->url_edit);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       widget = gtk_button_new_with_mnemonic (_("_Test URL..."));
+       gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_image_dialog_test_url), dialog);
+       dialog->priv->test_url_button = widget;
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_image_dialog_new (EHTMLEditor *editor)
+{
+       return GTK_WIDGET (
+               g_object_new (
+                       E_TYPE_HTML_EDITOR_IMAGE_DIALOG,
+                       "editor", editor,
+                       "title", N_("Image Properties"),
+                       NULL));
+}
+
+void
+e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog,
+                                 WebKitDOMNode *image)
+{
+       EHTMLEditorImageDialogClass *class;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_IMAGE_DIALOG (dialog));
+
+       if (image) {
+               dialog->priv->image = WEBKIT_DOM_HTML_IMAGE_ELEMENT (image);
+       } else {
+               dialog->priv->image = NULL;
+       }
+
+       class = E_HTML_EDITOR_IMAGE_DIALOG_GET_CLASS (dialog);
+       GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog));
+}
diff --git a/e-util/e-html-editor-image-dialog.h b/e-util/e-html-editor-image-dialog.h
new file mode 100644
index 0000000..efdbaf9
--- /dev/null
+++ b/e-util/e-html-editor-image-dialog.h
@@ -0,0 +1,73 @@
+/*
+ * e-html-editor-image-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_IMAGE_DIALOG_H
+#define E_HTML_EDITOR_IMAGE_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_IMAGE_DIALOG \
+       (e_html_editor_image_dialog_get_type ())
+#define E_HTML_EDITOR_IMAGE_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_IMAGE_DIALOG, EHTMLEditorImageDialog))
+#define E_HTML_EDITOR_IMAGE_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_IMAGE_DIALOG, EHTMLEditorImageDialogClass))
+#define E_IS_HTML_EDITOR_IMAGE_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_IMAGE_DIALOG))
+#define E_IS_HTML_EDITOR_IMAGE_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_IMAGE_DIALOG))
+#define E_HTML_EDITOR_IMAGE_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_IMAGE_DIALOG, EHTMLEditorImageDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorImageDialog EHTMLEditorImageDialog;
+typedef struct _EHTMLEditorImageDialogClass EHTMLEditorImageDialogClass;
+typedef struct _EHTMLEditorImageDialogPrivate EHTMLEditorImageDialogPrivate;
+
+struct _EHTMLEditorImageDialog {
+       EHTMLEditorDialog parent;
+       EHTMLEditorImageDialogPrivate *priv;
+};
+
+struct _EHTMLEditorImageDialogClass {
+       EHTMLEditorDialogClass parent_class;
+};
+
+GType          e_html_editor_image_dialog_get_type
+                                               (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_image_dialog_new  (EHTMLEditor *editor);
+void           e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog,
+                                                WebKitDOMNode *image);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_IMAGE_DIALOG_H */
+
diff --git a/e-util/e-html-editor-link-dialog.c b/e-util/e-html-editor-link-dialog.c
new file mode 100644
index 0000000..0572d07
--- /dev/null
+++ b/e-util/e-html-editor-link-dialog.c
@@ -0,0 +1,390 @@
+/*
+ * e-html-editor-link-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-link-dialog.h"
+#include "e-html-editor-selection.h"
+#include "e-html-editor-utils.h"
+#include "e-html-editor-view.h"
+
+#include <glib/gi18n-lib.h>
+
+#define E_HTML_EDITOR_LINK_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_LINK_DIALOG, EHTMLEditorLinkDialogPrivate))
+
+G_DEFINE_TYPE (
+       EHTMLEditorLinkDialog,
+       e_html_editor_link_dialog,
+       E_TYPE_HTML_EDITOR_DIALOG);
+
+struct _EHTMLEditorLinkDialogPrivate {
+       GtkWidget *url_edit;
+       GtkWidget *label_edit;
+       GtkWidget *test_button;
+
+       GtkWidget *remove_link_button;
+       GtkWidget *ok_button;
+
+       gboolean label_autofill;
+};
+
+static void
+html_editor_link_dialog_test_link (EHTMLEditorLinkDialog *dialog)
+{
+       gtk_show_uri (
+               gtk_window_get_screen (GTK_WINDOW (dialog)),
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)),
+               GDK_CURRENT_TIME,
+               NULL);
+}
+
+static void
+html_editor_link_dialog_url_changed (EHTMLEditorLinkDialog *dialog)
+{
+       if (dialog->priv->label_autofill &&
+           gtk_widget_is_sensitive (dialog->priv->label_edit)) {
+               const gchar *text;
+
+               text = gtk_entry_get_text (
+                       GTK_ENTRY (dialog->priv->url_edit));
+               gtk_entry_set_text (
+                       GTK_ENTRY (dialog->priv->label_edit), text);
+       }
+}
+
+static gboolean
+html_editor_link_dialog_description_changed (EHTMLEditorLinkDialog *dialog)
+{
+       const gchar *text;
+
+       text = gtk_entry_get_text (GTK_ENTRY (dialog->priv->label_edit));
+       dialog->priv->label_autofill = (*text == '\0');
+
+       return FALSE;
+}
+
+static void
+html_editor_link_dialog_remove_link (EHTMLEditorLinkDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+       e_html_editor_selection_unlink (selection);
+
+       gtk_widget_hide (GTK_WIDGET (dialog));
+}
+
+static void
+html_editor_link_dialog_ok (EHTMLEditorLinkDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+       WebKitDOMElement *link;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+
+       if (!dom_selection ||
+           (webkit_dom_dom_selection_get_range_count (dom_selection) == 0)) {
+               gtk_widget_hide (GTK_WIDGET (dialog));
+               return;
+       }
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       link = e_html_editor_dom_node_find_parent_element (
+                       webkit_dom_range_get_start_container (range, NULL), "A");
+       if (!link) {
+               if ((webkit_dom_range_get_start_container (range, NULL) !=
+                       webkit_dom_range_get_end_container (range, NULL)) ||
+                   (webkit_dom_range_get_start_offset (range, NULL) !=
+                       webkit_dom_range_get_end_offset (range, NULL))) {
+
+                       WebKitDOMDocumentFragment *fragment;
+                       fragment = webkit_dom_range_extract_contents (range, NULL);
+                       link = e_html_editor_dom_node_find_child_element (
+                               WEBKIT_DOM_NODE (fragment), "A");
+                       webkit_dom_range_insert_node (
+                               range, WEBKIT_DOM_NODE (fragment), NULL);
+
+                       webkit_dom_dom_selection_set_base_and_extent (
+                               dom_selection,
+                               webkit_dom_range_get_start_container (range, NULL),
+                               webkit_dom_range_get_start_offset (range, NULL),
+                               webkit_dom_range_get_end_container (range, NULL),
+                               webkit_dom_range_get_end_offset (range, NULL),
+                               NULL);
+               } else {
+                       /* get element that was clicked on */
+                       link = e_html_editor_view_get_element_under_mouse_click (view);
+                       if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link))
+                               link = NULL;
+               }
+       }
+
+       if (link) {
+               webkit_dom_html_anchor_element_set_href (
+                       WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link),
+                       gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)));
+               webkit_dom_html_element_set_inner_html (
+                       WEBKIT_DOM_HTML_ELEMENT (link),
+                       gtk_entry_get_text (GTK_ENTRY (dialog->priv->label_edit)),
+                       NULL);
+       } else {
+               gchar *text;
+
+               /* Check whether a text is selected or not */
+               text = webkit_dom_range_get_text (range);
+               if (text && *text) {
+                       e_html_editor_selection_create_link (
+                               selection,
+                               gtk_entry_get_text (
+                                       GTK_ENTRY (dialog->priv->url_edit)));
+               } else {
+                       gchar *html = g_strdup_printf (
+                               "<a href=\"%s\">%s</a>",
+                               gtk_entry_get_text (
+                                       GTK_ENTRY (dialog->priv->url_edit)),
+                               gtk_entry_get_text (
+                                       GTK_ENTRY (dialog->priv->label_edit)));
+
+                       e_html_editor_view_exec_command (
+                               view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, html);
+
+                       g_free (html);
+
+               }
+
+               g_free (text);
+       }
+
+       gtk_widget_hide (GTK_WIDGET (dialog));
+}
+
+static gboolean
+html_editor_link_dialog_entry_key_pressed (EHTMLEditorLinkDialog *dialog,
+                                           GdkEventKey *event)
+{
+       /* We can't do thins in key_released, because then you could not open
+        * this dialog from main menu by pressing enter on Insert->Link action */
+       if (event->keyval == GDK_KEY_Return) {
+               html_editor_link_dialog_ok (dialog);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+html_editor_link_dialog_show (GtkWidget *widget)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorLinkDialog *dialog;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+       WebKitDOMElement *link;
+
+       dialog = E_HTML_EDITOR_LINK_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+
+       /* Reset to default values */
+       gtk_entry_set_text (GTK_ENTRY (dialog->priv->url_edit), "http://";);
+       gtk_entry_set_text (GTK_ENTRY (dialog->priv->label_edit), "");
+       gtk_widget_set_sensitive (dialog->priv->label_edit, TRUE);
+       gtk_widget_set_sensitive (dialog->priv->remove_link_button, TRUE);
+       dialog->priv->label_autofill = TRUE;
+
+       /* No selection at all */
+       if (!dom_selection ||
+           webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
+               gtk_widget_set_sensitive (dialog->priv->remove_link_button, FALSE);
+               goto chainup;
+       }
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       link = e_html_editor_dom_node_find_parent_element (
+               webkit_dom_range_get_start_container (range, NULL), "A");
+       if (!link) {
+               if ((webkit_dom_range_get_start_container (range, NULL) !=
+                       webkit_dom_range_get_end_container (range, NULL)) ||
+                   (webkit_dom_range_get_start_offset (range, NULL) !=
+                       webkit_dom_range_get_end_offset (range, NULL))) {
+
+                       WebKitDOMDocumentFragment *fragment;
+                       fragment = webkit_dom_range_clone_contents (range, NULL);
+                       link = e_html_editor_dom_node_find_child_element (
+                                       WEBKIT_DOM_NODE (fragment), "A");
+               } else {
+                       /* get element that was clicked on */
+                       link = e_html_editor_view_get_element_under_mouse_click (view);
+                       if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link))
+                               link = NULL;
+               }
+       }
+
+       if (link) {
+               gchar *href, *text;
+
+               href = webkit_dom_html_anchor_element_get_href (
+                               WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link));
+               text = webkit_dom_html_element_get_inner_text (
+                               WEBKIT_DOM_HTML_ELEMENT (link));
+
+               gtk_entry_set_text (
+                       GTK_ENTRY (dialog->priv->url_edit), href);
+               gtk_entry_set_text (
+                       GTK_ENTRY (dialog->priv->label_edit), text);
+
+               g_free (text);
+               g_free (href);
+       } else {
+               gchar *text;
+
+               text = webkit_dom_range_get_text (range);
+               if (text && *text) {
+                       gtk_entry_set_text (
+                               GTK_ENTRY (dialog->priv->label_edit), text);
+                       gtk_widget_set_sensitive (
+                               dialog->priv->label_edit, FALSE);
+                       gtk_widget_set_sensitive (
+                               dialog->priv->remove_link_button, FALSE);
+               }
+               g_free (text);
+       }
+
+ chainup:
+       /* Chain up to parent implementation */
+       GTK_WIDGET_CLASS (e_html_editor_link_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_link_dialog_class_init (EHTMLEditorLinkDialogClass *class)
+{
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorLinkDialogPrivate));
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->show = html_editor_link_dialog_show;
+}
+
+static void
+e_html_editor_link_dialog_init (EHTMLEditorLinkDialog *dialog)
+{
+       GtkGrid *main_layout;
+       GtkBox *button_box;
+       GtkWidget *widget;
+
+       dialog->priv = E_HTML_EDITOR_LINK_DIALOG_GET_PRIVATE (dialog);
+
+       main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+       widget = gtk_entry_new ();
+       gtk_grid_attach (main_layout, widget, 1, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "notify::text",
+               G_CALLBACK (html_editor_link_dialog_url_changed), dialog);
+       g_signal_connect_swapped (
+               widget, "key-press-event",
+               G_CALLBACK (html_editor_link_dialog_entry_key_pressed), dialog);
+       dialog->priv->url_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_URL:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->url_edit);
+       gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+       widget = gtk_button_new_with_mnemonic (_("_Test URL..."));
+       gtk_grid_attach (main_layout, widget, 2, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_link_dialog_test_link), dialog);
+       dialog->priv->test_button = widget;
+
+       widget = gtk_entry_new ();
+       gtk_grid_attach (main_layout, widget, 1, 1, 2, 1);
+       g_signal_connect_swapped (
+               widget, "key-release-event",
+               G_CALLBACK (html_editor_link_dialog_description_changed), dialog);
+       g_signal_connect_swapped (
+               widget, "key-press-event",
+               G_CALLBACK (html_editor_link_dialog_entry_key_pressed), dialog);
+       dialog->priv->label_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Description:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->label_edit);
+       gtk_grid_attach (main_layout, widget, 0, 1, 1, 1);
+
+       button_box = e_html_editor_dialog_get_button_box (E_HTML_EDITOR_DIALOG (dialog));
+
+       widget = gtk_button_new_with_mnemonic (_("_Remove Link"));
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_link_dialog_remove_link), dialog);
+       gtk_box_pack_start (button_box, widget, FALSE, FALSE, 5);
+       dialog->priv->remove_link_button = widget;
+
+       widget = gtk_button_new_from_stock (GTK_STOCK_OK);
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_link_dialog_ok), dialog);
+       gtk_box_pack_end (button_box, widget, FALSE, FALSE, 5);
+       dialog->priv->ok_button = widget;
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_link_dialog_new (EHTMLEditor *editor)
+{
+       return GTK_WIDGET (
+               g_object_new (
+                       E_TYPE_HTML_EDITOR_LINK_DIALOG,
+                       "editor", editor,
+                       "icon-name", "insert-link",
+                       "title", N_("Link Properties"),
+                       NULL));
+}
diff --git a/e-util/e-html-editor-link-dialog.h b/e-util/e-html-editor-link-dialog.h
new file mode 100644
index 0000000..a1e426f
--- /dev/null
+++ b/e-util/e-html-editor-link-dialog.h
@@ -0,0 +1,70 @@
+/*
+ * e-html-editor-link-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_LINK_DIALOG_H
+#define E_HTML_EDITOR_LINK_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_LINK_DIALOG \
+       (e_html_editor_link_dialog_get_type ())
+#define E_HTML_EDITOR_LINK_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_LINK_DIALOG, EHTMLEditorLinkDialog))
+#define E_HTML_EDITOR_LINK_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_LINK_DIALOG, EHTMLEditorLinkDialogClass))
+#define E_IS_HTML_EDITOR_LINK_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_LINK_DIALOG))
+#define E_IS_HTML_EDITOR_LINK_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_LINK_DIALOG))
+#define E_HTML_EDITOR_LINK_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_LINK_DIALOG, EHTMLEditorLinkDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorLinkDialog EHTMLEditorLinkDialog;
+typedef struct _EHTMLEditorLinkDialogClass EHTMLEditorLinkDialogClass;
+typedef struct _EHTMLEditorLinkDialogPrivate EHTMLEditorLinkDialogPrivate;
+
+struct _EHTMLEditorLinkDialog {
+       EHTMLEditorDialog parent;
+       EHTMLEditorLinkDialogPrivate *priv;
+};
+
+struct _EHTMLEditorLinkDialogClass {
+       EHTMLEditorDialogClass parent_class;
+};
+
+GType          e_html_editor_link_dialog_get_type
+                                               (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_link_dialog_new   (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_LINK_DIALOG_H */
diff --git a/e-util/e-html-editor-manager.ui b/e-util/e-html-editor-manager.ui
new file mode 100644
index 0000000..75f2c34
--- /dev/null
+++ b/e-util/e-html-editor-manager.ui
@@ -0,0 +1,181 @@
+<ui>
+  <menubar name='main-menu'>
+    <placeholder name='pre-file-menu'/>
+    <menu action='file-menu'/>
+    <placeholder name='pre-edit-menu'/>
+    <menu action='edit-menu'>
+      <placeholder name='edit-menu-top'/>
+      <menuitem action='undo'/>
+      <menuitem action='redo'/>
+      <separator/>
+      <menuitem action='cut'/>
+      <menuitem action='copy'/>
+      <menuitem action='paste'/>
+      <menuitem action='paste-quote'/>
+      <separator/>
+      <menuitem action='select-all'/>
+      <separator/>
+      <menuitem action='show-find'/>
+      <menuitem action='find-again'/>
+      <menuitem action='show-replace'/>
+      <separator/>
+      <placeholder name='pre-spell-check'/>
+      <menuitem action='spell-check'/>
+      <menu action='language-menu'/>
+    </menu>
+    <placeholder name='pre-insert-menu'>
+      <menu action='view-menu'>
+        <placeholder name='view-menu-top'/>
+        <menuitem action='webkit-inspector'/>
+        <separator/>
+      </menu>
+    </placeholder>
+    <menu action='insert-menu'>
+      <placeholder name='insert-menu-top'/>
+      <menuitem action='insert-image'/>
+      <menuitem action='insert-link'/>
+      <menuitem action='insert-rule'/>
+      <menuitem action='insert-table'/>
+      <menuitem action='insert-text-file'/>
+      <menuitem action='insert-html-file'/>
+      <menuitem action='insert-emoticon'/>
+    </menu>
+    <placeholder name='pre-format-menu'/>
+    <menu action='format-menu'>
+      <placeholder name='format-menu-top'/>
+      <menuitem action='mode-html'/>
+      <menuitem action='mode-plain'/>
+      <separator/>
+      <menu action='font-style-menu'>
+        <menuitem action='monospaced'/>
+        <separator/>
+        <menuitem action='bold'/>
+        <menuitem action='italic'/>
+        <menuitem action='underline'/>
+        <menuitem action='strikethrough'/>
+      </menu>
+      <menu action='font-size-menu'>
+        <menuitem action='size-minus-two'/>
+        <menuitem action='size-minus-one'/>
+        <menuitem action='size-plus-zero'/>
+        <menuitem action='size-plus-one'/>
+        <menuitem action='size-plus-two'/>
+        <menuitem action='size-plus-three'/>
+        <menuitem action='size-plus-four'/>
+      </menu>
+      <separator/>
+      <menu action='paragraph-style-menu'>
+        <menuitem action='style-normal'/>
+        <separator/>
+        <menuitem action='style-h1'/>
+        <menuitem action='style-h2'/>
+        <menuitem action='style-h3'/>
+        <menuitem action='style-h4'/>
+        <menuitem action='style-h5'/>
+        <menuitem action='style-h6'/>
+        <separator/>
+        <menuitem action='style-list-bullet'/>
+        <menuitem action='style-list-roman'/>
+        <menuitem action='style-list-number'/>
+        <menuitem action='style-list-alpha'/>
+        <separator/>
+        <menuitem action='style-address'/>
+        <menuitem action='style-preformat'/>
+      </menu>
+      <menu action='justify-menu'>
+        <menuitem action='justify-left'/>
+        <menuitem action='justify-center'/>
+        <menuitem action='justify-right'/>
+      </menu>
+      <separator/>
+      <menuitem action='indent'/>
+      <menuitem action='unindent'/>
+      <menuitem action='wrap-lines'/>
+      <separator/>
+      <menuitem action='properties-page'/>
+    </menu>
+  </menubar>
+  <toolbar name='main-toolbar'>
+    <placeholder name='pre-main-toolbar'/>
+    <toolitem action='undo'/>
+    <toolitem action='redo'/>
+    <separator/>
+    <toolitem action='cut'/>
+    <toolitem action='copy'/>
+    <toolitem action='paste'/>
+    <separator/>
+    <toolitem action='show-find'/>
+    <toolitem action='show-replace'/>
+  </toolbar>
+  <toolbar name='edit-toolbar'>
+    <separator/>
+    <toolitem action='justify-left'/>
+    <toolitem action='justify-center'/>
+    <toolitem action='justify-right'/>
+    <separator/>
+    <toolitem action='unindent'/>
+    <toolitem action='indent'/>
+  </toolbar>
+  <toolbar name='html-toolbar'>
+    <separator/>
+    <toolitem action='monospaced'/>
+    <toolitem action='bold'/>
+    <toolitem action='italic'/>
+    <toolitem action='underline'/>
+    <toolitem action='strikethrough'/>
+    <separator/>
+    <toolitem action='insert-image'/>
+    <toolitem action='insert-link'/>
+    <toolitem action='insert-rule'/>
+    <toolitem action='insert-table'/>
+    <toolitem action='insert-emoticon'/>
+  </toolbar>
+  <popup name='context-menu'>
+    <placeholder name='context-spell-suggest'/>
+    <menu action='context-more-suggestions-menu'/>
+    <separator/>
+    <menuitem action='context-spell-ignore'/>
+    <menu action='context-spell-add-menu'/>
+    <menuitem action='context-spell-add'/>
+    <separator/>
+    <menuitem action='undo'/>
+    <menuitem action='redo'/>
+    <separator/>
+    <menuitem action='cut'/>
+    <menuitem action='copy'/>
+    <menuitem action='paste'/>
+    <menuitem action='paste-quote'/>
+    <separator/>
+    <menuitem action='context-insert-link'/>
+    <menuitem action='context-remove-link'/>
+    <separator/>
+    <menu action='context-properties-menu'>
+      <menuitem action='context-properties-text'/>
+      <menuitem action='context-properties-link'/>
+      <menuitem action='context-properties-rule'/>
+      <menuitem action='context-properties-image'/>
+      <menuitem action='context-properties-paragraph'/>
+      <menuitem action='context-properties-cell'/>
+      <menuitem action='context-properties-table'/>
+      <menuitem action='context-properties-page'/>
+    </menu>
+    <separator/>
+    <menu action='context-insert-table-menu'>
+      <menuitem action='context-insert-table'/>
+      <separator/>
+      <menuitem action='context-insert-row-above'/>
+      <menuitem action='context-insert-row-below'/>
+      <separator/>
+      <menuitem action='context-insert-column-before'/>
+      <menuitem action='context-insert-column-after'/>
+    </menu>
+    <menu action='context-delete-table-menu'>
+      <menuitem action='context-delete-table'/>
+      <menuitem action='context-delete-row'/>
+      <menuitem action='context-delete-column'/>
+      <menuitem action='context-delete-cell'/>
+    </menu>
+    <separator/>
+    <menu action='context-input-methods-menu'/>
+  </popup>
+</ui>
diff --git a/e-util/e-html-editor-page-dialog.c b/e-util/e-html-editor-page-dialog.c
new file mode 100644
index 0000000..53c8fce
--- /dev/null
+++ b/e-util/e-html-editor-page-dialog.c
@@ -0,0 +1,513 @@
+/*
+ * e-html-editor-page-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-page-dialog.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-color-combo.h"
+#include "e-misc-utils.h"
+
+#define E_HTML_EDITOR_PAGE_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_PAGE_DIALOG, EHTMLEditorPageDialogPrivate))
+
+struct _EHTMLEditorPageDialogPrivate {
+       GtkWidget *text_color_picker;
+       GtkWidget *link_color_picker;
+       GtkWidget *background_color_picker;
+
+       GtkWidget *background_template_combo;
+       GtkWidget *background_image_filechooser;
+};
+
+typedef struct _Template {
+       const gchar *name;
+       const gchar *filename;
+       GdkRGBA text_color;
+       GdkRGBA link_color;
+       GdkRGBA background_color;
+       gint left_margin;
+} Template;
+
+static const Template templates[] = {
+
+       {
+               N_("None"),
+               NULL,
+               { 0.0, 0.0 , 0.0 , 1 },
+               { 0.3, 0.55, 0.85, 1 },
+               { 1.0, 1.0 , 1.0 , 1 },
+               0
+       },
+
+       {
+               N_("Perforated Paper"),
+               "paper.png",
+               { 0.0, 0.0, 0.0, 1 },
+               { 0.0, 0.2, 0.4, 1 },
+               { 1.0, 1.0, 1.0, 0 },
+               30
+       },
+
+       {
+               N_("Blue Ink"),
+               "texture.png",
+               { 0.1, 0.12, 0.56, 1 },
+               { 0.0, 0.0,  1.0,  1 },
+               { 1.0, 1.0,  1.0,  1 },
+               0
+       },
+
+       {
+               N_("Paper"),
+               "rect.png",
+               { 0, 0, 0, 1 },
+               { 0, 0, 1, 1 },
+               { 1, 1, 1, 1 },
+               0
+       },
+
+       {
+               N_("Ribbon"),
+               "ribbon.jpg",
+               { 0.0, 0.0, 0.0, 1 },
+               { 0.6, 0.2, 0.4, 1 },
+               { 1.0, 1.0, 1.0, 1 },
+               70
+       },
+
+       {
+               N_("Midnight"),
+               "midnight-stars.jpg",
+               { 1.0, 1.0, 1.0, 1 },
+               { 1.0, 0.6, 0.0, 1 },
+               { 0.0, 0.0, 0.0, 1 },
+               0
+       },
+
+       {
+               N_("Confidential"),
+               "confidential-stamp.jpg",
+               { 0.0, 0.0, 0.0, 1 },
+               { 0.0, 0.0, 1.0, 1 },
+               { 1.0, 1.0, 1.0, 1 },
+               0
+       },
+
+       {
+               N_("Draft"),
+               "draft-stamp.jpg",
+               { 0.0, 0.0, 0.0, 1 },
+               { 0.0, 0.0, 1.0, 1 },
+               { 1.0, 1.0, 1.0, 1 },
+               0
+       },
+
+       {
+               N_("Graph Paper"),
+               "draft-paper.png",
+               { 0.0 , 0.0 , 0.5,  1 },
+               { 0.88, 0.13, 0.14, 1 },
+               { 1.0 , 1.0 , 1.0 , 1 },
+               0
+       }
+};
+
+G_DEFINE_TYPE (
+       EHTMLEditorPageDialog,
+       e_html_editor_page_dialog,
+       E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+html_editor_page_dialog_set_text_color (EHTMLEditorPageDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       GdkRGBA rgba;
+       gchar *color;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+
+       e_color_combo_get_current_color (
+               E_COLOR_COMBO (dialog->priv->text_color_picker), &rgba);
+
+       color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
+       webkit_dom_html_body_element_set_text (
+               WEBKIT_DOM_HTML_BODY_ELEMENT (body), color);
+
+       g_free (color);
+}
+
+static void
+html_editor_page_dialog_set_link_color (EHTMLEditorPageDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       GdkRGBA rgba;
+       gchar *color;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+
+       e_color_combo_get_current_color (
+               E_COLOR_COMBO (dialog->priv->link_color_picker), &rgba);
+
+       color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
+       webkit_dom_html_body_element_set_link (
+               WEBKIT_DOM_HTML_BODY_ELEMENT (body), color);
+
+       g_free (color);
+}
+
+static void
+html_editor_page_dialog_set_background_color (EHTMLEditorPageDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       GdkRGBA rgba;
+       gchar *color;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+
+       e_color_combo_get_current_color (
+               E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
+
+       color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
+
+       webkit_dom_html_body_element_set_bg_color (
+               WEBKIT_DOM_HTML_BODY_ELEMENT (body), color);
+
+       g_free (color);
+}
+
+static void
+html_editor_page_dialog_set_background_from_template (EHTMLEditorPageDialog *dialog)
+{
+       const Template *tmplt;
+
+       tmplt = &templates[
+               gtk_combo_box_get_active (
+                       GTK_COMBO_BOX (dialog->priv->background_template_combo))];
+
+       /* Special case - 'none' template */
+       if (tmplt->filename == NULL) {
+               gtk_file_chooser_unselect_all (
+                       GTK_FILE_CHOOSER (dialog->priv->background_image_filechooser));
+       } else {
+               gchar *filename;
+
+               e_color_combo_set_current_color (
+                       E_COLOR_COMBO (dialog->priv->text_color_picker),
+                       &tmplt->text_color);
+               e_color_combo_set_current_color (
+                       E_COLOR_COMBO (dialog->priv->background_color_picker),
+                       &tmplt->background_color);
+               e_color_combo_set_current_color (
+                       E_COLOR_COMBO (dialog->priv->link_color_picker),
+                       &tmplt->link_color);
+
+               filename = g_build_filename (EVOLUTION_IMAGESDIR, tmplt->filename, NULL);
+
+               gtk_file_chooser_set_filename (
+                       GTK_FILE_CHOOSER (dialog->priv->background_image_filechooser),
+                       filename);
+               g_free (filename);
+       }
+
+}
+
+static void
+html_editor_page_dialog_set_background_image (EHTMLEditorPageDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       gchar *uri;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+
+       uri = gtk_file_chooser_get_uri (
+                       GTK_FILE_CHOOSER (
+                               dialog->priv->background_image_filechooser));
+
+       webkit_dom_html_body_element_set_background (
+               WEBKIT_DOM_HTML_BODY_ELEMENT (body), uri ? uri : "");
+
+       g_free (uri);
+}
+
+static void
+html_editor_page_dialog_show (GtkWidget *widget)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorPageDialog *dialog;
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       gchar *tmp;
+       GdkRGBA rgba;
+
+       dialog = E_HTML_EDITOR_PAGE_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+
+       tmp = webkit_dom_html_body_element_get_background (
+                       WEBKIT_DOM_HTML_BODY_ELEMENT (body));
+       if (tmp && *tmp) {
+               gint ii;
+               gchar *fname = g_filename_from_uri (tmp, NULL, NULL);
+               for (ii = 0; ii < G_N_ELEMENTS (templates); ii++) {
+                       const Template *tmplt = &templates[ii];
+
+                       if (g_strcmp0 (tmplt->filename, fname) == 0) {
+                               gtk_combo_box_set_active (
+                                       GTK_COMBO_BOX (dialog->priv->background_template_combo),
+                                       ii);
+                               break;
+                       }
+               }
+               g_free (fname);
+       } else {
+               gtk_combo_box_set_active (
+                       GTK_COMBO_BOX (dialog->priv->background_template_combo), 0);
+       }
+       g_free (tmp);
+
+       tmp = webkit_dom_html_body_element_get_text (
+                       WEBKIT_DOM_HTML_BODY_ELEMENT (body));
+       if (!tmp || !*tmp) {
+               GdkColor *color;
+               GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (view));
+               color = &style->text[GTK_STATE_NORMAL];
+
+               rgba.alpha = 1;
+               rgba.red = ((gdouble) color->red) / G_MAXUINT16;
+               rgba.green = ((gdouble) color->green) / G_MAXUINT16;
+               rgba.blue = ((gdouble) color->blue) / G_MAXUINT16;
+       } else {
+               gdk_rgba_parse (&rgba, tmp);
+       }
+       g_free (tmp);
+       e_color_combo_set_current_color (
+               E_COLOR_COMBO (dialog->priv->text_color_picker), &rgba);
+
+       tmp = webkit_dom_html_body_element_get_link (
+                       WEBKIT_DOM_HTML_BODY_ELEMENT (body));
+       if (!tmp || !*tmp) {
+               GdkColor color;
+               gtk_widget_style_get (
+                       GTK_WIDGET (view), "link-color", &color, NULL);
+
+               rgba.alpha = 1;
+               rgba.red = ((gdouble) color.red) / G_MAXUINT16;
+               rgba.green = ((gdouble) color.green) / G_MAXUINT16;
+               rgba.blue = ((gdouble) color.blue) / G_MAXUINT16;
+       } else {
+               gdk_rgba_parse (&rgba, tmp);
+       }
+       g_free (tmp);
+       e_color_combo_set_current_color (
+               E_COLOR_COMBO (dialog->priv->link_color_picker), &rgba);
+
+       tmp = webkit_dom_html_body_element_get_bg_color (
+                       WEBKIT_DOM_HTML_BODY_ELEMENT (body));
+       if (!tmp || !*tmp) {
+               GdkColor *color;
+               GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (view));
+               color = &style->base[GTK_STATE_NORMAL];
+
+               rgba.alpha = 1;
+               rgba.red = ((gdouble) color->red) / G_MAXUINT16;
+               rgba.green = ((gdouble) color->green) / G_MAXUINT16;
+               rgba.blue = ((gdouble) color->blue) / G_MAXUINT16;
+
+       } else {
+               gdk_rgba_parse (&rgba, tmp);
+       }
+       g_free (tmp);
+       e_color_combo_set_current_color (
+               E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
+
+       GTK_WIDGET_CLASS (e_html_editor_page_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_page_dialog_class_init (EHTMLEditorPageDialogClass *class)
+{
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorPageDialogPrivate));
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->show = html_editor_page_dialog_show;
+}
+
+static void
+e_html_editor_page_dialog_init (EHTMLEditorPageDialog *dialog)
+{
+       GtkGrid *grid, *main_layout;
+       GtkWidget *widget;
+       gint ii;
+
+       dialog->priv = E_HTML_EDITOR_PAGE_DIALOG_GET_PRIVATE (dialog);
+
+       main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+       /* == Colors == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Colors</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Text */
+       widget = e_color_combo_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       g_signal_connect_swapped (
+               widget, "notify::current-color",
+               G_CALLBACK (html_editor_page_dialog_set_text_color), dialog);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       dialog->priv->text_color_picker = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Text:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (
+               GTK_LABEL (widget), dialog->priv->text_color_picker);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       /* Link */
+       widget = e_color_combo_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       g_signal_connect_swapped (
+               widget, "notify::current-color",
+               G_CALLBACK (html_editor_page_dialog_set_link_color), dialog);
+       gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+       dialog->priv->link_color_picker = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Link:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (
+               GTK_LABEL (widget), dialog->priv->link_color_picker);
+       gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+       /* Background */
+       widget = e_color_combo_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       g_signal_connect_swapped (
+               widget, "notify::current-color",
+               G_CALLBACK (html_editor_page_dialog_set_background_color), dialog);
+       gtk_grid_attach (grid, widget, 1, 2, 1, 1);
+       dialog->priv->background_color_picker = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Background:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (
+               GTK_LABEL (widget), dialog->priv->background_color_picker);
+       gtk_grid_attach (grid, widget, 0, 2, 1, 1);
+
+       /* == Background Image == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Background Image</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Template */
+       widget = gtk_combo_box_text_new ();
+       for (ii = 0; ii < G_N_ELEMENTS (templates); ii++) {
+               gtk_combo_box_text_append_text (
+                       GTK_COMBO_BOX_TEXT (widget), templates[ii].name);
+       }
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_page_dialog_set_background_from_template), dialog);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       dialog->priv->background_template_combo = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Template:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (
+               GTK_LABEL (widget), dialog->priv->background_template_combo);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       /* Custom image */
+       widget = gtk_file_chooser_button_new (
+               _("Selection a file"), GTK_FILE_CHOOSER_ACTION_OPEN);
+       g_signal_connect_swapped (
+               widget, "selection-changed",
+               G_CALLBACK (html_editor_page_dialog_set_background_image), dialog);
+       gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+       dialog->priv->background_image_filechooser = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Custom:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (
+               GTK_LABEL (widget), dialog->priv->background_image_filechooser);
+       gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_page_dialog_new (EHTMLEditor *editor)
+{
+       return GTK_WIDGET (
+               g_object_new (
+                       E_TYPE_HTML_EDITOR_PAGE_DIALOG,
+                       "editor", editor,
+                       "title", N_("Page Properties"),
+                       NULL));
+}
diff --git a/e-util/e-html-editor-page-dialog.h b/e-util/e-html-editor-page-dialog.h
new file mode 100644
index 0000000..5678efc
--- /dev/null
+++ b/e-util/e-html-editor-page-dialog.h
@@ -0,0 +1,70 @@
+/*
+ * e-html-editor-page-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_PAGE_DIALOG_H
+#define E_HTML_EDITOR_PAGE_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_PAGE_DIALOG \
+       (e_html_editor_page_dialog_get_type ())
+#define E_HTML_EDITOR_PAGE_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_PAGE_DIALOG, EHTMLEditorPageDialog))
+#define E_HTML_EDITOR_PAGE_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_PAGE_DIALOG, EHTMLEditorPageDialogClass))
+#define E_IS_HTML_EDITOR_PAGE_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_PAGE_DIALOG))
+#define E_IS_HTML_EDITOR_PAGE_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_PAGE_DIALOG))
+#define E_HTML_EDITOR_PAGE_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_PAGE_DIALOG, EHTMLEditorPageDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorPageDialog EHTMLEditorPageDialog;
+typedef struct _EHTMLEditorPageDialogClass EHTMLEditorPageDialogClass;
+typedef struct _EHTMLEditorPageDialogPrivate EHTMLEditorPageDialogPrivate;
+
+struct _EHTMLEditorPageDialog {
+       EHTMLEditorDialog parent;
+       EHTMLEditorPageDialogPrivate *priv;
+};
+
+struct _EHTMLEditorPageDialogClass {
+       EHTMLEditorDialogClass parent_class;
+};
+
+GType          e_html_editor_page_dialog_get_type
+                                               (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_page_dialog_new   (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_PAGE_DIALOG_H */
diff --git a/e-util/e-html-editor-paragraph-dialog.c b/e-util/e-html-editor-paragraph-dialog.c
new file mode 100644
index 0000000..f0fce97
--- /dev/null
+++ b/e-util/e-html-editor-paragraph-dialog.c
@@ -0,0 +1,154 @@
+/*
+ * e-html-editor-paragraph-dialog.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-paragraph-dialog.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-action-combo-box.h"
+
+#define E_HTML_EDITOR_PARAGRAPH_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG, EHTMLEditorParagraphDialogPrivate))
+
+G_DEFINE_TYPE (
+       EHTMLEditorParagraphDialog,
+       e_html_editor_paragraph_dialog,
+       E_TYPE_HTML_EDITOR_DIALOG);
+
+struct _EHTMLEditorParagraphDialogPrivate {
+       GtkWidget *style_combo;
+
+       GtkWidget *left_button;
+       GtkWidget *center_button;
+       GtkWidget *right_button;
+};
+
+static void
+html_editor_paragraph_dialog_constructed (GObject *object)
+{
+       GtkGrid *main_layout, *grid;
+       GtkWidget *widget;
+       EHTMLEditor *editor;
+       EHTMLEditorParagraphDialog *dialog;
+
+       dialog = E_HTML_EDITOR_PARAGRAPH_DIALOG (object);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+
+       main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+       /* == General == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>General</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Style */
+       widget = e_action_combo_box_new_with_action (
+               GTK_RADIO_ACTION (e_html_editor_get_action (editor, "style-normal")));
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       dialog->priv->style_combo = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Style:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->style_combo);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       /* == Alignment == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Alignment</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Left */
+       widget = gtk_toggle_button_new_with_label (GTK_STOCK_JUSTIFY_LEFT);
+       gtk_button_set_use_stock (GTK_BUTTON (widget), TRUE);
+       gtk_activatable_set_related_action (
+               GTK_ACTIVATABLE (widget),
+               e_html_editor_get_action (editor, "justify-left"));
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+       dialog->priv->left_button = widget;
+
+       /* Center */
+       widget = gtk_toggle_button_new_with_label (GTK_STOCK_JUSTIFY_CENTER);
+       gtk_button_set_use_stock (GTK_BUTTON (widget), TRUE);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       gtk_activatable_set_related_action (
+               GTK_ACTIVATABLE (widget),
+               e_html_editor_get_action (editor, "justify-center"));
+       dialog->priv->center_button = widget;
+
+       /* Right */
+       widget = gtk_toggle_button_new_with_label (GTK_STOCK_JUSTIFY_RIGHT);
+       gtk_button_set_use_stock (GTK_BUTTON (widget), TRUE);
+       gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+       gtk_activatable_set_related_action (
+               GTK_ACTIVATABLE (widget),
+               e_html_editor_get_action (editor, "justify-right"));
+       dialog->priv->right_button = widget;
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+static void
+e_html_editor_paragraph_dialog_class_init (EHTMLEditorParagraphDialogClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (
+               class, sizeof (EHTMLEditorParagraphDialogPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->constructed = html_editor_paragraph_dialog_constructed;
+}
+
+static void
+e_html_editor_paragraph_dialog_init (EHTMLEditorParagraphDialog *dialog)
+{
+       dialog->priv = E_HTML_EDITOR_PARAGRAPH_DIALOG_GET_PRIVATE (dialog);
+}
+
+GtkWidget *
+e_html_editor_paragraph_dialog_new (EHTMLEditor *editor)
+{
+       return GTK_WIDGET (
+               g_object_new (
+                       E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG,
+                       "editor", editor,
+                       "title", N_("Paragraph Properties"),
+                       NULL));
+}
diff --git a/e-util/e-html-editor-paragraph-dialog.h b/e-util/e-html-editor-paragraph-dialog.h
new file mode 100644
index 0000000..17855bd
--- /dev/null
+++ b/e-util/e-html-editor-paragraph-dialog.h
@@ -0,0 +1,71 @@
+/*
+ * e-html-editor-paragraph-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_PARAGRAPH_DIALOG_H
+#define E_HTML_EDITOR_PARAGRAPH_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG \
+       (e_html_editor_paragraph_dialog_get_type ())
+#define E_HTML_EDITOR_PARAGRAPH_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG, EHTMLEditorParagraphDialog))
+#define E_HTML_EDITOR_PARAGRAPH_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG, EHTMLEditorParagraphDialogClass))
+#define E_IS_HTML_EDITOR_PARAGRAPH_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG))
+#define E_IS_HTML_EDITOR_PARAGRAPH_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG))
+#define E_HTML_EDITOR_PARAGRAPH_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_PARAGRAPH_DIALOG, EHTMLEditorParagraphDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorParagraphDialog EHTMLEditorParagraphDialog;
+typedef struct _EHTMLEditorParagraphDialogClass EHTMLEditorParagraphDialogClass;
+typedef struct _EHTMLEditorParagraphDialogPrivate EHTMLEditorParagraphDialogPrivate;
+
+struct _EHTMLEditorParagraphDialog {
+       EHTMLEditorDialog parent;
+       EHTMLEditorParagraphDialogPrivate *priv;
+};
+
+struct _EHTMLEditorParagraphDialogClass {
+       EHTMLEditorDialogClass parent_class;
+};
+
+GType          e_html_editor_paragraph_dialog_get_type
+                                               (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_paragraph_dialog_new
+                                               (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_PARAGRAPH_DIALOG_H */
diff --git a/e-util/e-html-editor-private.h b/e-util/e-html-editor-private.h
new file mode 100644
index 0000000..dc4658b
--- /dev/null
+++ b/e-util/e-html-editor-private.h
@@ -0,0 +1,103 @@
+/*
+ * e-html-editor-private.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef E_HTML_EDITOR_PRIVATE_H
+#define E_HTML_EDITOR_PRIVATE_H
+
+#include <e-action-combo-box.h>
+#include <e-color-combo.h>
+#include <e-html-editor.h>
+#include <e-html-editor-actions.h>
+#include <e-html-editor-cell-dialog.h>
+#include <e-html-editor-find-dialog.h>
+#include <e-html-editor-hrule-dialog.h>
+#include <e-html-editor-image-dialog.h>
+#include <e-html-editor-link-dialog.h>
+#include <e-html-editor-page-dialog.h>
+#include <e-html-editor-paragraph-dialog.h>
+#include <e-html-editor-replace-dialog.h>
+#include <e-html-editor-spell-check-dialog.h>
+#include <e-html-editor-table-dialog.h>
+#include <e-html-editor-text-dialog.h>
+#include <e-html-editor-view.h>
+
+#ifdef HAVE_XFREE
+#include <X11/XF86keysym.h>
+#endif
+
+#define ACTION(name) (E_HTML_EDITOR_ACTION_##name (editor))
+#define WIDGET(name) (E_HTML_EDITOR_WIDGETS_##name (editor))
+
+G_BEGIN_DECLS
+
+struct _EHTMLEditorPrivate {
+       GtkUIManager *manager;
+       GtkActionGroup *core_actions;
+       GtkActionGroup *html_actions;
+       GtkActionGroup *context_actions;
+       GtkActionGroup *html_context_actions;
+       GtkActionGroup *language_actions;
+       GtkActionGroup *spell_check_actions;
+       GtkActionGroup *suggestion_actions;
+
+       GtkWidget *main_menu;
+       GtkWidget *main_toolbar;
+       GtkWidget *edit_toolbar;
+       GtkWidget *html_toolbar;
+       GtkWidget *activity_bar;
+       GtkWidget *alert_bar;
+       GtkWidget *edit_area;
+
+       GtkWidget *find_dialog;
+       GtkWidget *replace_dialog;
+       GtkWidget *link_dialog;
+       GtkWidget *hrule_dialog;
+       GtkWidget *table_dialog;
+       GtkWidget *page_dialog;
+       GtkWidget *image_dialog;
+       GtkWidget *text_dialog;
+       GtkWidget *paragraph_dialog;
+       GtkWidget *cell_dialog;
+       GtkWidget *spell_check_dialog;
+
+       GtkWidget *color_combo_box;
+       GtkWidget *mode_combo_box;
+       GtkWidget *size_combo_box;
+       GtkWidget *style_combo_box;
+       GtkWidget *scrolled_window;
+
+       EHTMLEditorView *html_editor_view;
+       EHTMLEditorSelection *selection;
+
+       gchar *filename;
+
+       guint spell_suggestions_merge_id;
+
+       WebKitDOMNode *image;
+       WebKitDOMNode *table_cell;
+
+       gint editor_layout_row;
+};
+
+void           editor_actions_init             (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_PRIVATE_H */
diff --git a/e-util/e-html-editor-replace-dialog.c b/e-util/e-html-editor-replace-dialog.c
new file mode 100644
index 0000000..7addcdf
--- /dev/null
+++ b/e-util/e-html-editor-replace-dialog.c
@@ -0,0 +1,288 @@
+/*
+ * e-html-editor-replace-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-replace-dialog.h"
+
+#include <glib/gi18n-lib.h>
+
+#define E_HTML_EDITOR_REPLACE_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_REPLACE_DIALOG, EHTMLEditorReplaceDialogPrivate))
+
+G_DEFINE_TYPE (
+       EHTMLEditorReplaceDialog,
+       e_html_editor_replace_dialog,
+       E_TYPE_HTML_EDITOR_DIALOG);
+
+struct _EHTMLEditorReplaceDialogPrivate {
+       GtkWidget *search_entry;
+       GtkWidget *replace_entry;
+
+       GtkWidget *case_sensitive;
+       GtkWidget *backwards;
+       GtkWidget *wrap;
+
+       GtkWidget *result_label;
+
+       GtkWidget *skip_button;
+       GtkWidget *replace_button;
+       GtkWidget *replace_all_button;
+
+       EHTMLEditor *editor;
+};
+
+enum {
+       PROP_0,
+       PROP_EDITOR
+};
+
+static gboolean
+jump (EHTMLEditorReplaceDialog *dialog)
+{
+       EHTMLEditor *editor;
+       WebKitWebView *webview;
+       gboolean found;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       webview = WEBKIT_WEB_VIEW (
+                       e_html_editor_get_view (editor));
+
+       found = webkit_web_view_search_text (
+               webview,
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry)),
+               gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->case_sensitive)),
+               !gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->backwards)),
+               gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->wrap)));
+
+       return found;
+}
+
+static void
+html_editor_replace_dialog_skip_cb (EHTMLEditorReplaceDialog *dialog)
+{
+       if (!jump (dialog)) {
+               gtk_label_set_label (
+                       GTK_LABEL (dialog->priv->result_label),
+                       N_("No match found"));
+               gtk_widget_show (dialog->priv->result_label);
+       } else {
+               gtk_widget_hide (dialog->priv->result_label);
+       }
+}
+
+static void
+html_editor_replace_dialog_replace_cb (EHTMLEditorReplaceDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+
+       /* Jump to next matching word */
+       if (!jump (dialog)) {
+               gtk_label_set_label (
+                       GTK_LABEL (dialog->priv->result_label),
+                       N_("No match found"));
+               gtk_widget_show (dialog->priv->result_label);
+               return;
+       } else {
+               gtk_widget_hide (dialog->priv->result_label);
+       }
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       e_html_editor_selection_replace (
+               selection,
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry)));
+}
+
+static void
+html_editor_replace_dialog_replace_all_cb (EHTMLEditorReplaceDialog *dialog)
+{
+       gint i = 0;
+       gchar *result;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+       const gchar *replacement;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+       replacement = gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry));
+
+       while (jump (dialog)) {
+               e_html_editor_selection_replace (selection, replacement);
+               i++;
+
+               /* Jump behind the word */
+               e_html_editor_selection_move (
+                       selection, TRUE, E_HTML_EDITOR_SELECTION_GRANULARITY_WORD);
+       }
+
+       result = g_strdup_printf (_("%d occurences replaced"), i);
+       gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), result);
+       gtk_widget_show (dialog->priv->result_label);
+       g_free (result);
+}
+
+static void
+html_editor_replace_dialog_entry_changed (EHTMLEditorReplaceDialog *dialog)
+{
+       gboolean ready;
+       ready = ((gtk_entry_get_text_length (
+                       GTK_ENTRY (dialog->priv->search_entry)) != 0) &&
+                (gtk_entry_get_text_length (
+                        GTK_ENTRY (dialog->priv->replace_entry)) != 0));
+
+       gtk_widget_set_sensitive (dialog->priv->skip_button, ready);
+       gtk_widget_set_sensitive (dialog->priv->replace_button, ready);
+       gtk_widget_set_sensitive (dialog->priv->replace_all_button, ready);
+}
+
+static void
+html_editor_replace_dialog_show (GtkWidget *widget)
+{
+       EHTMLEditorReplaceDialog *dialog = E_HTML_EDITOR_REPLACE_DIALOG (widget);
+
+       gtk_widget_grab_focus (dialog->priv->search_entry);
+       gtk_widget_hide (dialog->priv->result_label);
+
+       /* Chain up to parent implementation */
+       GTK_WIDGET_CLASS (e_html_editor_replace_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_replace_dialog_class_init (EHTMLEditorReplaceDialogClass *class)
+{
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorReplaceDialogPrivate));
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->show = html_editor_replace_dialog_show;
+}
+
+static void
+e_html_editor_replace_dialog_init (EHTMLEditorReplaceDialog *dialog)
+{
+       GtkGrid *main_layout;
+       GtkWidget *widget, *layout;
+       GtkBox *button_box;
+
+       dialog->priv = E_HTML_EDITOR_REPLACE_DIALOG_GET_PRIVATE (dialog);
+
+       main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+       widget = gtk_entry_new ();
+       gtk_grid_attach (main_layout, widget, 1, 0, 2, 1);
+       dialog->priv->search_entry = widget;
+       g_signal_connect_swapped (
+               widget, "notify::text-length",
+               G_CALLBACK (html_editor_replace_dialog_entry_changed), dialog);
+
+       widget = gtk_label_new_with_mnemonic (_("R_eplace:"));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->search_entry);
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+       widget = gtk_entry_new ();
+       gtk_grid_attach (main_layout, widget, 1, 1, 2, 1);
+       dialog->priv->replace_entry = widget;
+       g_signal_connect_swapped (
+               widget, "notify::text-length",
+               G_CALLBACK (html_editor_replace_dialog_entry_changed), dialog);
+
+       widget = gtk_label_new_with_mnemonic (_("_With:"));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->replace_entry);
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_grid_attach (main_layout, widget, 0, 1, 1, 1);
+
+       layout = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+       gtk_grid_attach (main_layout, layout, 1, 2, 2, 1);
+
+       widget = gtk_check_button_new_with_mnemonic (_("Search _backwards"));
+       gtk_box_pack_start (GTK_BOX (layout), widget, FALSE, FALSE, 0);
+       dialog->priv->backwards = widget;
+
+       widget = gtk_check_button_new_with_mnemonic (_("_Case sensitive"));
+       gtk_box_pack_start (GTK_BOX (layout), widget, FALSE, FALSE, 0);
+       dialog->priv->case_sensitive = widget;
+
+       widget = gtk_check_button_new_with_mnemonic (_("Wra_p search"));
+       gtk_box_pack_start (GTK_BOX (layout), widget, FALSE, FALSE, 0);
+       dialog->priv->wrap = widget;
+
+       widget = gtk_label_new ("");
+       gtk_grid_attach (main_layout, widget, 0, 3, 2, 1);
+       dialog->priv->result_label = widget;
+
+       button_box = e_html_editor_dialog_get_button_box (E_HTML_EDITOR_DIALOG (dialog));
+
+       widget = gtk_button_new_with_mnemonic (_("_Skip"));
+       gtk_box_pack_start (button_box, widget, FALSE, FALSE, 5);
+       gtk_widget_set_sensitive (widget, FALSE);
+       dialog->priv->skip_button = widget;
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_replace_dialog_skip_cb), dialog);
+
+       widget = gtk_button_new_with_mnemonic (_("_Replace"));
+       gtk_box_pack_start (button_box, widget, FALSE, FALSE, 5);
+       gtk_widget_set_sensitive (widget, FALSE);
+       dialog->priv->replace_button = widget;
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_replace_dialog_replace_cb), dialog);
+
+       widget = gtk_button_new_with_mnemonic (_("Replace _All"));
+       gtk_box_pack_start (button_box, widget, FALSE, FALSE, 5);
+       gtk_widget_set_sensitive (widget, FALSE);
+       dialog->priv->replace_all_button = widget;
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_replace_dialog_replace_all_cb), dialog);
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_replace_dialog_new (EHTMLEditor *editor)
+{
+       return GTK_WIDGET (
+               g_object_new (
+                       E_TYPE_HTML_EDITOR_REPLACE_DIALOG,
+                       "editor", editor,
+                       "icon-name", GTK_STOCK_FIND_AND_REPLACE,
+                       "resizable", FALSE,
+                       "title", N_("Replace"),
+                       "transient-for", gtk_widget_get_toplevel (GTK_WIDGET (editor)),
+                       "type", GTK_WINDOW_TOPLEVEL,
+                       "window-position", GTK_WIN_POS_CENTER_ON_PARENT,
+                       NULL));
+}
diff --git a/e-util/e-html-editor-replace-dialog.h b/e-util/e-html-editor-replace-dialog.h
new file mode 100644
index 0000000..f253e42
--- /dev/null
+++ b/e-util/e-html-editor-replace-dialog.h
@@ -0,0 +1,71 @@
+/*
+ * e-html-editor-replace-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_REPLACE_DIALOG_H
+#define E_HTML_EDITOR_REPLACE_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_REPLACE_DIALOG \
+       (e_html_editor_replace_dialog_get_type ())
+#define E_HTML_EDITOR_REPLACE_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_REPLACE_DIALOG, EHTMLEditorReplaceDialog))
+#define E_HTML_EDITOR_REPLACE_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_REPLACE_DIALOG, EHTMLEditorReplaceDialogClass))
+#define E_IS_HTML_EDITOR_REPLACE_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_REPLACE_DIALOG))
+#define E_IS_HTML_EDITOR_REPLACE_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_REPLACE_DIALOG))
+#define E_HTML_EDITOR_REPLACE_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_REPLACE_DIALOG, EHTMLEditorReplaceDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorReplaceDialog EHTMLEditorReplaceDialog;
+typedef struct _EHTMLEditorReplaceDialogClass EHTMLEditorReplaceDialogClass;
+typedef struct _EHTMLEditorReplaceDialogPrivate EHTMLEditorReplaceDialogPrivate;
+
+struct _EHTMLEditorReplaceDialog {
+       EHTMLEditorDialog parent;
+       EHTMLEditorReplaceDialogPrivate *priv;
+};
+
+struct _EHTMLEditorReplaceDialogClass {
+       EHTMLEditorDialogClass parent_class;
+};
+
+GType          e_html_editor_replace_dialog_get_type
+                                               (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_replace_dialog_new        (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_REPLACE_DIALOG_H */
+
diff --git a/e-util/e-html-editor-selection.c b/e-util/e-html-editor-selection.c
new file mode 100644
index 0000000..c109063
--- /dev/null
+++ b/e-util/e-html-editor-selection.c
@@ -0,0 +1,5576 @@
+/*
+ * e-html-editor-selection.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-selection.h"
+#include "e-html-editor-view.h"
+#include "e-html-editor.h"
+#include "e-html-editor-utils.h"
+
+#include <e-util/e-util.h>
+
+#include <webkit/webkit.h>
+#include <webkit/webkitdom.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#define E_HTML_EDITOR_SELECTION_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelectionPrivate))
+
+#define UNICODE_ZERO_WIDTH_SPACE "\xe2\x80\x8b"
+#define UNICODE_NBSP "\xc2\xa0"
+
+#define SPACES_PER_INDENTATION 4
+#define SPACES_PER_LIST_LEVEL 8
+
+/**
+ * EHTMLEditorSelection
+ *
+ * The #EHTMLEditorSelection object represents current position of the cursor
+ * with the editor or current text selection within the editor. To obtain
+ * valid #EHTMLEditorSelection, call e_html_editor_view_get_selection().
+ */
+
+struct _EHTMLEditorSelectionPrivate {
+
+       GWeakRef html_editor_view;
+       gulong selection_changed_handler_id;
+
+       gchar *text;
+
+       gboolean is_bold;
+       gboolean is_italic;
+       gboolean is_underline;
+       gboolean is_monospaced;
+       gboolean is_strikethrough;
+
+       gchar *background_color;
+       gchar *font_color;
+       gchar *font_family;
+
+       gulong selection_offset;
+
+       gint word_wrap_length;
+       guint font_size;
+
+       EHTMLEditorSelectionAlignment alignment;
+};
+
+enum {
+       PROP_0,
+       PROP_ALIGNMENT,
+       PROP_BACKGROUND_COLOR,
+       PROP_BLOCK_FORMAT,
+       PROP_BOLD,
+       PROP_HTML_EDITOR_VIEW,
+       PROP_FONT_COLOR,
+       PROP_FONT_NAME,
+       PROP_FONT_SIZE,
+       PROP_INDENTED,
+       PROP_ITALIC,
+       PROP_MONOSPACED,
+       PROP_STRIKETHROUGH,
+       PROP_SUBSCRIPT,
+       PROP_SUPERSCRIPT,
+       PROP_TEXT,
+       PROP_UNDERLINE
+};
+
+static const GdkRGBA black = { 0 };
+
+G_DEFINE_TYPE (
+       EHTMLEditorSelection,
+       e_html_editor_selection,
+       G_TYPE_OBJECT
+);
+
+static WebKitDOMRange *
+html_editor_selection_get_current_range (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+       WebKitWebView *web_view;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range = NULL;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_val_if_fail (view != NULL, NULL);
+
+       web_view = WEBKIT_WEB_VIEW (view);
+
+       document = webkit_web_view_get_dom_document (web_view);
+       window = webkit_dom_document_get_default_view (document);
+       if (!window)
+               goto exit;
+
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+       if (!WEBKIT_DOM_IS_DOM_SELECTION (dom_selection))
+               goto exit;
+
+       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1)
+               goto exit;
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+ exit:
+       g_object_unref (view);
+
+       return range;
+}
+
+static gboolean
+get_has_style (EHTMLEditorSelection *selection,
+               const gchar *style_tag)
+{
+       WebKitDOMNode *node;
+       WebKitDOMElement *element;
+       WebKitDOMRange *range;
+       gboolean result;
+       gint tag_len;
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return FALSE;
+
+       node = webkit_dom_range_get_start_container (range, NULL);
+       if (WEBKIT_DOM_IS_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = webkit_dom_node_get_parent_element (node);
+
+       tag_len = strlen (style_tag);
+       result = FALSE;
+       while (!result && element) {
+               gchar *element_tag;
+               gboolean accept_citation = FALSE;
+
+               element_tag = webkit_dom_element_get_tag_name (element);
+
+               if (g_ascii_strncasecmp (style_tag, "citation", 8) == 0) {
+                       accept_citation = TRUE;
+                       result = ((strlen (element_tag) == 10 /* strlen ("blockquote") */) &&
+                               (g_ascii_strncasecmp (element_tag, "blockquote", 10) == 0));
+                       if (element_has_class (element, "-x-evo-indented"))
+                               result = FALSE;
+               } else {
+                       result = ((tag_len == strlen (element_tag)) &&
+                               (g_ascii_strncasecmp (element_tag, style_tag, tag_len) == 0));
+               }
+
+               /* Special case: <blockquote type=cite> marks quotation, while
+                * just <blockquote> is used for indentation. If the <blockquote>
+                * has type=cite, then ignore it unless style_tag is "citation" */
+               if (result && g_ascii_strncasecmp (element_tag, "blockquote", 10) == 0) {
+                       if (webkit_dom_element_has_attribute (element, "type")) {
+                               gchar *type;
+                               type = webkit_dom_element_get_attribute (element, "type");
+                               if (!accept_citation && (g_ascii_strncasecmp (type, "cite", 4) == 0)) {
+                                       result = FALSE;
+                               }
+                               g_free (type);
+                       } else {
+                               if (accept_citation)
+                                       result = FALSE;
+                       }
+               }
+
+               g_free (element_tag);
+
+               if (result)
+                       break;
+
+               element = webkit_dom_node_get_parent_element (
+                       WEBKIT_DOM_NODE (element));
+       }
+
+       return result;
+}
+
+static gchar *
+get_font_property (EHTMLEditorSelection *selection,
+                   const gchar *font_property)
+{
+       WebKitDOMRange *range;
+       WebKitDOMNode *node;
+       WebKitDOMElement *element;
+       gchar *value;
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return NULL;
+
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+       element = e_html_editor_dom_node_find_parent_element (node, "FONT");
+       if (!element)
+               return NULL;
+
+       g_object_get (G_OBJECT (element), font_property, &value, NULL);
+
+       return value;
+}
+
+static void
+html_editor_selection_selection_changed_cb (WebKitWebView *webview,
+                                            EHTMLEditorSelection *selection)
+{
+       g_object_freeze_notify (G_OBJECT (selection));
+
+       g_object_notify (G_OBJECT (selection), "alignment");
+       g_object_notify (G_OBJECT (selection), "background-color");
+       g_object_notify (G_OBJECT (selection), "bold");
+       g_object_notify (G_OBJECT (selection), "font-name");
+       g_object_notify (G_OBJECT (selection), "font-size");
+       g_object_notify (G_OBJECT (selection), "font-color");
+       g_object_notify (G_OBJECT (selection), "block-format");
+       g_object_notify (G_OBJECT (selection), "indented");
+       g_object_notify (G_OBJECT (selection), "italic");
+       g_object_notify (G_OBJECT (selection), "monospaced");
+       g_object_notify (G_OBJECT (selection), "strikethrough");
+       g_object_notify (G_OBJECT (selection), "subscript");
+       g_object_notify (G_OBJECT (selection), "superscript");
+       g_object_notify (G_OBJECT (selection), "text");
+       g_object_notify (G_OBJECT (selection), "underline");
+
+       g_object_thaw_notify (G_OBJECT (selection));
+}
+
+void
+e_html_editor_selection_block_selection_changed (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_signal_handlers_block_by_func (
+               view, html_editor_selection_selection_changed_cb, selection);
+       g_object_unref (view);
+}
+
+void
+e_html_editor_selection_unblock_selection_changed (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_signal_handlers_unblock_by_func (
+               view, html_editor_selection_selection_changed_cb, selection);
+       g_object_unref (view);
+}
+
+static void
+html_editor_selection_set_html_editor_view (EHTMLEditorSelection *selection,
+                                            EHTMLEditorView *view)
+{
+       gulong handler_id;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+       g_weak_ref_set (&selection->priv->html_editor_view, view);
+
+       handler_id = g_signal_connect (
+               view, "selection-changed",
+               G_CALLBACK (html_editor_selection_selection_changed_cb),
+               selection);
+
+       selection->priv->selection_changed_handler_id = handler_id;
+}
+
+static void
+html_editor_selection_get_property (GObject *object,
+                                    guint property_id,
+                                    GValue *value,
+                                    GParamSpec *pspec)
+{
+       GdkRGBA rgba = { 0 };
+
+       switch (property_id) {
+               case PROP_ALIGNMENT:
+                       g_value_set_int (
+                               value,
+                               e_html_editor_selection_get_alignment (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_BACKGROUND_COLOR:
+                       g_value_set_string (
+                               value,
+                               e_html_editor_selection_get_background_color (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_BLOCK_FORMAT:
+                       g_value_set_int (
+                               value,
+                               e_html_editor_selection_get_block_format (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_BOLD:
+                       g_value_set_boolean (
+                               value,
+                               e_html_editor_selection_is_bold (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_HTML_EDITOR_VIEW:
+                       g_value_take_object (
+                               value,
+                               e_html_editor_selection_ref_html_editor_view (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_FONT_COLOR:
+                       e_html_editor_selection_get_font_color (
+                               E_HTML_EDITOR_SELECTION (object), &rgba);
+                       g_value_set_boxed (value, &rgba);
+                       return;
+
+               case PROP_FONT_NAME:
+                       g_value_set_string (
+                               value,
+                               e_html_editor_selection_get_font_name (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_FONT_SIZE:
+                       g_value_set_int (
+                               value,
+                               e_html_editor_selection_get_font_size (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_INDENTED:
+                       g_value_set_boolean (
+                               value,
+                               e_html_editor_selection_is_indented (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_ITALIC:
+                       g_value_set_boolean (
+                               value,
+                               e_html_editor_selection_is_italic (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_MONOSPACED:
+                       g_value_set_boolean (
+                               value,
+                               e_html_editor_selection_is_monospaced (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_STRIKETHROUGH:
+                       g_value_set_boolean (
+                               value,
+                               e_html_editor_selection_is_strikethrough (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_SUBSCRIPT:
+                       g_value_set_boolean (
+                               value,
+                               e_html_editor_selection_is_subscript (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_SUPERSCRIPT:
+                       g_value_set_boolean (
+                               value,
+                               e_html_editor_selection_is_superscript (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+
+               case PROP_TEXT:
+                       g_value_set_string (
+                               value,
+                               e_html_editor_selection_get_string (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       break;
+
+               case PROP_UNDERLINE:
+                       g_value_set_boolean (
+                               value,
+                               e_html_editor_selection_is_underline (
+                               E_HTML_EDITOR_SELECTION (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_selection_set_property (GObject *object,
+                                    guint property_id,
+                                    const GValue *value,
+                                    GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_ALIGNMENT:
+                       e_html_editor_selection_set_alignment (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_int (value));
+                       return;
+
+               case PROP_BACKGROUND_COLOR:
+                       e_html_editor_selection_set_background_color (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_BOLD:
+                       e_html_editor_selection_set_bold (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_HTML_EDITOR_VIEW:
+                       html_editor_selection_set_html_editor_view (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_object (value));
+                       return;
+
+               case PROP_FONT_COLOR:
+                       e_html_editor_selection_set_font_color (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_boxed (value));
+                       return;
+
+               case PROP_BLOCK_FORMAT:
+                       e_html_editor_selection_set_block_format (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_int (value));
+                       return;
+
+               case PROP_FONT_NAME:
+                       e_html_editor_selection_set_font_name (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_FONT_SIZE:
+                       e_html_editor_selection_set_font_size (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_int (value));
+                       return;
+
+               case PROP_ITALIC:
+                       e_html_editor_selection_set_italic (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_MONOSPACED:
+                       e_html_editor_selection_set_monospaced (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_STRIKETHROUGH:
+                       e_html_editor_selection_set_strikethrough (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_SUBSCRIPT:
+                       e_html_editor_selection_set_subscript (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_SUPERSCRIPT:
+                       e_html_editor_selection_set_superscript (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_UNDERLINE:
+                       e_html_editor_selection_set_underline (
+                               E_HTML_EDITOR_SELECTION (object),
+                               g_value_get_boolean (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_selection_dispose (GObject *object)
+{
+       EHTMLEditorSelectionPrivate *priv;
+       EHTMLEditorView *view;
+
+       priv = E_HTML_EDITOR_SELECTION_GET_PRIVATE (object);
+
+       view = g_weak_ref_get (&priv->html_editor_view);
+       if (view != NULL) {
+               g_signal_handler_disconnect (
+                       view, priv->selection_changed_handler_id);
+               priv->selection_changed_handler_id = 0;
+               g_object_unref (view);
+       }
+
+       g_weak_ref_set (&priv->html_editor_view, NULL);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_html_editor_selection_parent_class)->dispose (object);
+}
+
+static void
+html_editor_selection_finalize (GObject *object)
+{
+       EHTMLEditorSelection *selection = E_HTML_EDITOR_SELECTION (object);
+
+       g_free (selection->priv->text);
+       g_free (selection->priv->background_color);
+       g_free (selection->priv->font_color);
+       g_free (selection->priv->font_family);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_html_editor_selection_parent_class)->finalize (object);
+}
+
+static void
+e_html_editor_selection_class_init (EHTMLEditorSelectionClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorSelectionPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->get_property = html_editor_selection_get_property;
+       object_class->set_property = html_editor_selection_set_property;
+       object_class->dispose = html_editor_selection_dispose;
+       object_class->finalize = html_editor_selection_finalize;
+
+       /**
+        * EHTMLEditorSelectionalignment
+        *
+        * Holds alignment of current paragraph.
+        */
+       /* FIXME: Convert the enum to a proper type */
+       g_object_class_install_property (
+               object_class,
+               PROP_ALIGNMENT,
+               g_param_spec_int (
+                       "alignment",
+                       NULL,
+                       NULL,
+                       E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
+                       E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT,
+                       E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
+                       G_PARAM_READWRITE));
+
+       /**
+        * EHTMLEditorSelectionbackground-color
+        *
+        * Holds background color of current selection or at current cursor
+        * position.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_BACKGROUND_COLOR,
+               g_param_spec_string (
+                       "background-color",
+                       NULL,
+                       NULL,
+                       NULL,
+                       G_PARAM_READWRITE));
+
+       /**
+        * EHTMLEditorSelectionblock-format
+        *
+        * Holds block format of current paragraph. See
+        * #EHTMLEditorSelectionBlockFormat for valid values.
+        */
+       /* FIXME Convert the EHTMLEditorSelectionBlockFormat
+        *       enum to a proper type. */
+       g_object_class_install_property (
+               object_class,
+               PROP_BLOCK_FORMAT,
+               g_param_spec_int (
+                       "block-format",
+                       NULL,
+                       NULL,
+                       0,
+                       G_MAXINT,
+                       0,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectionbold
+        *
+        * Holds whether current selection or text at current cursor position
+        * is bold.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_BOLD,
+               g_param_spec_boolean (
+                       "bold",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_HTML_EDITOR_VIEW,
+               g_param_spec_object (
+                       "html-editor-view",
+                       NULL,
+                       NULL,
+                       E_TYPE_HTML_EDITOR_VIEW,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectionfont-color
+        *
+        * Holds font color of current selection or at current cursor position.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_FONT_COLOR,
+               g_param_spec_boxed (
+                       "font-color",
+                       NULL,
+                       NULL,
+                       GDK_TYPE_RGBA,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectionfont-name
+        *
+        * Holds name of font in current selection or at current cursor
+        * position.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_FONT_NAME,
+               g_param_spec_string (
+                       "font-name",
+                       NULL,
+                       NULL,
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectionfont-size
+        *
+        * Holds point size of current selection or at current cursor position.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_FONT_SIZE,
+               g_param_spec_int (
+                       "font-size",
+                       NULL,
+                       NULL,
+                       1,
+                       7,
+                       3,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectionindented
+        *
+        * Holds whether current paragraph is indented. This does not include
+        * citations.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_INDENTED,
+               g_param_spec_boolean (
+                       "indented",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectionitalic
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is italic.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_ITALIC,
+               g_param_spec_boolean (
+                       "italic",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectionmonospaced
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is monospaced.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_MONOSPACED,
+               g_param_spec_boolean (
+                       "monospaced",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectionstrikethrough
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is strikethrough.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_STRIKETHROUGH,
+               g_param_spec_boolean (
+                       "strikethrough",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectionsuperscript
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is in superscript.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_SUPERSCRIPT,
+               g_param_spec_boolean (
+                       "superscript",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectionsubscript
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is in subscript.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_SUBSCRIPT,
+               g_param_spec_boolean (
+                       "subscript",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectiontext
+        *
+        * Holds always up-to-date text of current selection.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_TEXT,
+               g_param_spec_string (
+                       "text",
+                       NULL,
+                       NULL,
+                       NULL,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorSelectionunderline
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is underlined.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_UNDERLINE,
+               g_param_spec_boolean (
+                       "underline",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_html_editor_selection_init (EHTMLEditorSelection *selection)
+{
+       GSettings *g_settings;
+
+       selection->priv = E_HTML_EDITOR_SELECTION_GET_PRIVATE (selection);
+
+       g_settings = g_settings_new ("org.gnome.evolution.mail");
+       selection->priv->word_wrap_length =
+               g_settings_get_int (g_settings, "composer-word-wrap-length");
+       g_object_unref (g_settings);
+}
+
+gint
+e_html_editor_selection_get_word_wrap_length (EHTMLEditorSelection *selection)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), 72);
+
+       return selection->priv->word_wrap_length;
+}
+
+/**
+ * e_html_editor_selection_ref_html_editor_view:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns a new reference to @selection's #EHTMLEditorView.  Unreference
+ * the #EHTMLEditorView with g_object_unref() when finished with it.
+ *
+ * Returns: an #EHTMLEditorView
+ **/
+EHTMLEditorView *
+e_html_editor_selection_ref_html_editor_view (EHTMLEditorSelection *selection)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+       return g_weak_ref_get (&selection->priv->html_editor_view);
+}
+
+/**
+ * e_html_editor_selection_has_text:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection contains any text.
+ *
+ * Returns: @TRUE when current selection contains text, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_has_text (EHTMLEditorSelection *selection)
+{
+       WebKitDOMRange *range;
+       WebKitDOMNode *node;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+       range = html_editor_selection_get_current_range (selection);
+
+       node = webkit_dom_range_get_start_container (range, NULL);
+       if (webkit_dom_node_get_node_type (node) == 3)
+               return TRUE;
+
+       node = webkit_dom_range_get_end_container (range, NULL);
+       if (webkit_dom_node_get_node_type (node) == 3)
+               return TRUE;
+
+       node = WEBKIT_DOM_NODE (webkit_dom_range_clone_contents (range, NULL));
+       while (node) {
+               if (webkit_dom_node_get_node_type (node) == 3)
+                       return TRUE;
+
+               if (webkit_dom_node_has_child_nodes (node)) {
+                       node = webkit_dom_node_get_first_child (node);
+               } else if (webkit_dom_node_get_next_sibling (node)) {
+                       node = webkit_dom_node_get_next_sibling (node);
+               } else {
+                       node = webkit_dom_node_get_parent_node (node);
+                       if (node) {
+                               node = webkit_dom_node_get_next_sibling (node);
+                       }
+               }
+       }
+
+       return FALSE;
+}
+
+/**
+ * e_html_editor_selection_get_caret_word:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns word under cursor.
+ *
+ * Returns: A newly allocated string with current caret word or @NULL when there
+ * is no text under cursor or when selection is active. [transfer-full].
+ */
+gchar *
+e_html_editor_selection_get_caret_word (EHTMLEditorSelection *selection)
+{
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+       range = html_editor_selection_get_current_range (selection);
+
+       /* Don't operate on the visible selection */
+       range = webkit_dom_range_clone_range (range, NULL);
+       webkit_dom_range_expand (range, "word", NULL);
+
+       return webkit_dom_range_to_string (range, NULL);
+}
+
+/**
+ * e_html_editor_selection_replace_caret_word:
+ * @selection: an #EHTMLEditorSelection
+ * @replacement: a string to replace current caret word with
+ *
+ * Replaces current word under cursor with @replacement.
+ */
+void
+e_html_editor_selection_replace_caret_word (EHTMLEditorSelection *selection,
+                                            const gchar *replacement)
+{
+       EHTMLEditorView *view;
+       WebKitWebView *web_view;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+       g_return_if_fail (replacement != NULL);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       web_view = WEBKIT_WEB_VIEW (view);
+
+       range = html_editor_selection_get_current_range (selection);
+       document = webkit_web_view_get_dom_document (web_view);
+       window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+
+       webkit_dom_range_expand (range, "word", NULL);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+       e_html_editor_selection_insert_html (selection, replacement);
+
+       g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_get_string:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns currently selected string.
+ *
+ * Returns: A pointer to content of current selection. The string is owned by
+ * #EHTMLEditorSelection and should not be free'd.
+ */
+const gchar *
+e_html_editor_selection_get_string (EHTMLEditorSelection *selection)
+{
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return NULL;
+
+       g_free (selection->priv->text);
+       selection->priv->text = webkit_dom_range_get_text (range);
+
+       return selection->priv->text;
+}
+
+/**
+ * e_html_editor_selection_replace:
+ * @selection: an #EHTMLEditorSelection
+ * @new_string: a string to replace current selection with
+ *
+ * Replaces currently selected text with @new_string.
+ */
+void
+e_html_editor_selection_replace (EHTMLEditorSelection *selection,
+                                 const gchar *new_string)
+{
+       EHTMLEditorView *view;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       e_html_editor_view_exec_command (
+               view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, new_string);
+
+       g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_get_list_alignment_from_node:
+ * @node: #an WebKitDOMNode
+ *
+ * Returns alignment of given list.
+ *
+ * Returns: #EHTMLEditorSelectionAlignment
+ */
+EHTMLEditorSelectionAlignment
+e_html_editor_selection_get_list_alignment_from_node (WebKitDOMNode *node)
+{
+       if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-list-item-align-left"))
+               return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+       if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-list-item-align-center"))
+               return E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER;
+       if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-list-item-align-right"))
+               return E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT;
+
+       return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+}
+
+/**
+ * e_html_editor_selection_get_alignment:
+ * @selection: #an EHTMLEditorSelection
+ *
+ * Returns alignment of current paragraph
+ *
+ * Returns: #EHTMLEditorSelectionAlignment
+ */
+EHTMLEditorSelectionAlignment
+e_html_editor_selection_get_alignment (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorSelectionAlignment alignment;
+       EHTMLEditorView *view;
+       gchar *value;
+       WebKitDOMCSSStyleDeclaration *style;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMElement *element;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (
+               E_IS_HTML_EDITOR_SELECTION (selection),
+               E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_val_if_fail (view != NULL, FALSE);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       g_object_unref (view);
+       window = webkit_dom_document_get_default_view (document);
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+
+       node = webkit_dom_range_get_start_container (range, NULL);
+       if (!node)
+               return E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+
+       if (WEBKIT_DOM_IS_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = webkit_dom_node_get_parent_element (node);
+
+       style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+       value = webkit_dom_css_style_declaration_get_property_value (style, "text-align");
+
+       if (!value || !*value ||
+           (g_ascii_strncasecmp (value, "left", 4) == 0)) {
+               alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+       } else if (g_ascii_strncasecmp (value, "center", 6) == 0) {
+               alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER;
+       } else if (g_ascii_strncasecmp (value, "right", 5) == 0) {
+               alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT;
+       } else {
+               alignment = E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT;
+       }
+
+       g_free (value);
+
+       return alignment;
+}
+
+static void
+set_ordered_list_type_to_element (WebKitDOMElement *list,
+                                  EHTMLEditorSelectionBlockFormat format)
+{
+       if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST)
+               webkit_dom_element_remove_attribute (list, "type");
+       else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA)
+               webkit_dom_element_set_attribute (list, "type", "A", NULL);
+       else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN)
+               webkit_dom_element_set_attribute (list, "type", "I", NULL);
+}
+
+static WebKitDOMElement *
+create_list_element (EHTMLEditorSelection *selection,
+                     WebKitDOMDocument *document,
+                     EHTMLEditorSelectionBlockFormat format,
+                    gint level,
+                     gboolean html_mode)
+{
+       WebKitDOMElement *list;
+       gint offset = -SPACES_PER_LIST_LEVEL;
+       gboolean inserting_unordered_list =
+               format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
+
+       list = webkit_dom_document_create_element (
+               document, inserting_unordered_list  ? "UL" : "OL", NULL);
+
+       set_ordered_list_type_to_element (list, format);
+
+       if (level >= 0)
+               offset = (level + 1) * -SPACES_PER_LIST_LEVEL;
+
+       if (!html_mode)
+               e_html_editor_selection_set_paragraph_style (
+                       selection, list, -1, offset, "");
+
+       return list;
+}
+
+static void
+remove_node (WebKitDOMNode *node)
+{
+       webkit_dom_node_remove_child (
+               webkit_dom_node_get_parent_node (node), node, NULL);
+}
+
+static void
+format_change_list_from_list (EHTMLEditorSelection *selection,
+                              WebKitDOMDocument *document,
+                              EHTMLEditorSelectionBlockFormat to,
+                              gboolean html_mode)
+{
+       gboolean after_selection_end = FALSE;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker, *new_list;
+       WebKitDOMNode *source_list, *source_list_clone, *current_list, *item;
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       if (!selection_start_marker || !selection_end_marker)
+               return;
+
+       new_list = create_list_element (selection, document, to, 0, html_mode);
+
+       /* Copy elements from previous block to list */
+       item = webkit_dom_node_get_parent_node (
+               WEBKIT_DOM_NODE (selection_start_marker));
+       source_list = webkit_dom_node_get_parent_node (item);
+       current_list = source_list;
+       source_list_clone = webkit_dom_node_clone_node (source_list, FALSE);
+
+       if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented"))
+               element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented");
+
+       while (item) {
+               WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item);
+
+               if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
+                       webkit_dom_node_append_child (
+                               after_selection_end ?
+                                       source_list_clone : WEBKIT_DOM_NODE (new_list),
+                               WEBKIT_DOM_NODE (item),
+                               NULL);
+               }
+
+               if (webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end_marker))) {
+                       source_list_clone = webkit_dom_node_clone_node (current_list, FALSE);
+                       after_selection_end = TRUE;
+               }
+
+               if (!next_item) {
+                       if (after_selection_end)
+                               break;
+                       current_list = webkit_dom_node_get_next_sibling (current_list);
+                       next_item = webkit_dom_node_get_first_child (current_list);
+               }
+               item = next_item;
+       }
+
+       if (webkit_dom_node_has_child_nodes (source_list_clone))
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (source_list),
+                       WEBKIT_DOM_NODE (source_list_clone),
+                       webkit_dom_node_get_next_sibling (source_list), NULL);
+       if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (new_list)))
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (source_list),
+                       WEBKIT_DOM_NODE (new_list),
+                       webkit_dom_node_get_next_sibling (source_list), NULL);
+       if (!webkit_dom_node_has_child_nodes (source_list))
+               remove_node (source_list);
+}
+
+/**
+ * e_html_editor_selection_set_alignment:
+ * @selection: an #EHTMLEditorSelection
+ * @alignment: an #EHTMLEditorSelectionAlignment value to apply
+ *
+ * Sets alignment of current paragraph to give @alignment.
+ */
+void
+e_html_editor_selection_set_alignment (EHTMLEditorSelection *selection,
+                                       EHTMLEditorSelectionAlignment alignment)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+       const gchar *class = "";
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker;
+       WebKitDOMNode *parent;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       if (e_html_editor_selection_get_alignment (selection) == alignment)
+               return;
+
+       switch (alignment) {
+               case E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER:
+                       command = E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER;
+                       class  = "-x-evo-list-item-align-center";
+                       break;
+
+               case E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT:
+                       command = E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_LEFT;
+                       break;
+
+               case E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT:
+                       command = E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT;
+                       class  = "-x-evo-list-item-align-right";
+                       break;
+       }
+
+       selection->priv->alignment = alignment;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       e_html_editor_selection_save (selection);
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+
+       if (!selection_start_marker) {
+               g_object_unref (view);
+               return;
+       }
+
+       parent = webkit_dom_node_get_parent_node (
+               WEBKIT_DOM_NODE (selection_start_marker));
+
+       if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (parent)) {
+               element_remove_class (
+                       WEBKIT_DOM_ELEMENT (parent),
+                       "-x-evo-list-item-align-center");
+               element_remove_class (
+                       WEBKIT_DOM_ELEMENT (parent),
+                       "-x-evo-list-item-align-right");
+
+               element_add_class (WEBKIT_DOM_ELEMENT (parent), class);
+       } else {
+               e_html_editor_view_exec_command (view, command, NULL);
+       }
+
+       e_html_editor_selection_restore (selection);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "alignment");
+}
+
+/**
+ * e_html_editor_selection_get_background_color:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns background color of currently selected text or letter at current
+ * cursor position.
+ *
+ * Returns: A string with code of current background color.
+ */
+const gchar *
+e_html_editor_selection_get_background_color (EHTMLEditorSelection *selection)
+{
+       WebKitDOMNode *ancestor;
+       WebKitDOMRange *range;
+       WebKitDOMCSSStyleDeclaration *css;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+       range = html_editor_selection_get_current_range (selection);
+
+       ancestor = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+       css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (ancestor));
+       selection->priv->background_color =
+               webkit_dom_css_style_declaration_get_property_value (
+                       css, "background-color");
+
+       return selection->priv->background_color;
+}
+
+/**
+ * e_html_editor_selection_set_background_color:
+ * @selection: an #EHTMLEditorSelection
+ * @color: code of new background color to set
+ *
+ * Changes background color of current selection or letter at current cursor
+ * position to @color.
+ */
+void
+e_html_editor_selection_set_background_color (EHTMLEditorSelection *selection,
+                                              const gchar *color)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+       g_return_if_fail (color != NULL && *color != '\0');
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR;
+       e_html_editor_view_exec_command (view, command, color);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "background-color");
+}
+
+static gint
+get_indentation_level (WebKitDOMElement *element)
+{
+       WebKitDOMElement *parent;
+       gint level = 0;
+
+       if (element_has_class (element, "-x-evo-indented"))
+               level++;
+
+       parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element));
+       /* Count level of indentation */
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               if (element_has_class (parent, "-x-evo-indented"))
+                       level++;
+
+               parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (parent));
+       }
+
+       return level;
+}
+
+static WebKitDOMNode *
+get_block_node (WebKitDOMRange *range)
+{
+       WebKitDOMNode *node;
+
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+       if (!WEBKIT_DOM_IS_ELEMENT (node))
+               node = WEBKIT_DOM_NODE (webkit_dom_node_get_parent_element (node));
+
+       if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-temp-text-wrapper"))
+               node = WEBKIT_DOM_NODE (webkit_dom_node_get_parent_element (node));
+
+       return node;
+}
+
+/**
+ * e_html_editor_selection_get_list_format_from_node:
+ * @node: an #WebKitDOMNode
+ *
+ * Returns block format of given list.
+ *
+ * Returns: #EHTMLEditorSelectionBlockFormat
+ */
+EHTMLEditorSelectionBlockFormat
+e_html_editor_selection_get_list_format_from_node (WebKitDOMNode *node)
+{
+       EHTMLEditorSelectionBlockFormat format =
+               E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
+
+       if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node))
+               return -1;
+
+       if (WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (node))
+               return format;
+
+       if (WEBKIT_DOM_IS_ELEMENT (node)) {
+               gchar *type_value = webkit_dom_element_get_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "type");
+
+               if (!type_value)
+                       return E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST;
+
+               if (!*type_value)
+                       format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST;
+               else if (g_ascii_strcasecmp (type_value, "A") == 0)
+                       format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA;
+               else if (g_ascii_strcasecmp (type_value, "I") == 0)
+                       format = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN;
+               g_free (type_value);
+       }
+
+       return -1;
+}
+
+/**
+ * e_html_editor_selection_get_block_format:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns block format of current paragraph.
+ *
+ * Returns: #EHTMLEditorSelectionBlockFormat
+ */
+EHTMLEditorSelectionBlockFormat
+e_html_editor_selection_get_block_format (EHTMLEditorSelection *selection)
+{
+       WebKitDOMNode *node;
+       WebKitDOMRange *range;
+       WebKitDOMElement *element;
+       EHTMLEditorSelectionBlockFormat result;
+
+       g_return_val_if_fail (
+               E_IS_HTML_EDITOR_SELECTION (selection),
+               E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH);
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
+
+       node = webkit_dom_range_get_start_container (range, NULL);
+
+       if (e_html_editor_dom_node_find_parent_element (node, "UL")) {
+               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
+       } else if ((element = e_html_editor_dom_node_find_parent_element (node, "OL")) != NULL) {
+               if (webkit_dom_element_has_attribute (element, "type")) {
+                       gchar *type;
+
+                       type = webkit_dom_element_get_attribute (element, "type");
+                       if (type && ((*type == 'a') || (*type == 'A'))) {
+                               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA;
+                       } else if (type && ((*type == 'i') || (*type == 'I'))) {
+                               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN;
+                       } else {
+                               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST;
+                       }
+
+                       g_free (type);
+               } else {
+                       result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST;
+               }
+       } else if (e_html_editor_dom_node_find_parent_element (node, "PRE")) {
+               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE;
+       } else if (e_html_editor_dom_node_find_parent_element (node, "ADDRESS")) {
+               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS;
+       } else if (e_html_editor_dom_node_find_parent_element (node, "H1")) {
+               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1;
+       } else if (e_html_editor_dom_node_find_parent_element (node, "H2")) {
+               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2;
+       } else if (e_html_editor_dom_node_find_parent_element (node, "H3")) {
+               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3;
+       } else if (e_html_editor_dom_node_find_parent_element (node, "H4")) {
+               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4;
+       } else if (e_html_editor_dom_node_find_parent_element (node, "H5")) {
+               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5;
+       } else if (e_html_editor_dom_node_find_parent_element (node, "H6")) {
+               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6;
+       } else if ((element = e_html_editor_dom_node_find_parent_element (node, "BLOCKQUOTE")) != NULL) {
+               if (element_has_class (element, "-x-evo-indented"))
+                       result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
+               else {
+                       WebKitDOMNode *block = get_block_node (range);
+
+                       if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-paragraph"))
+                               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
+                       else
+                               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE;
+               }
+       } else if (e_html_editor_dom_node_find_parent_element (node, "P")) {
+               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
+       } else {
+               result = E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH;
+       }
+
+       return result;
+}
+
+static gboolean
+is_caret_position_node (WebKitDOMNode *node)
+{
+       return element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-caret-position");
+}
+
+static void
+merge_list_into_list (WebKitDOMNode *from,
+                      WebKitDOMNode *to,
+                      gboolean insert_before)
+{
+       WebKitDOMNode *item;
+
+       if (!(to && from))
+               return;
+
+       while ((item = webkit_dom_node_get_first_child (from)) != NULL) {
+               if (insert_before)
+                       webkit_dom_node_insert_before (
+                               to, item, webkit_dom_node_get_last_child (to), NULL);
+               else
+                       webkit_dom_node_append_child (to, item, NULL);
+       }
+
+       if (!webkit_dom_node_has_child_nodes (from))
+               remove_node (from);
+
+}
+
+static void
+merge_lists_if_possible (WebKitDOMNode *list)
+{
+       EHTMLEditorSelectionBlockFormat format, prev, next;
+       WebKitDOMNode *prev_sibling, *next_sibling;
+
+       prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (list));
+       next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (list));
+
+       format = e_html_editor_selection_get_list_format_from_node (list),
+       prev = e_html_editor_selection_get_list_format_from_node (prev_sibling);
+       next = e_html_editor_selection_get_list_format_from_node (next_sibling);
+
+       if (format == prev && format != -1 && prev != -1)
+               merge_list_into_list (prev_sibling, list, TRUE);
+
+       if (format == next && format != -1 && next != -1)
+               merge_list_into_list (next_sibling, list, FALSE);
+}
+
+static void
+remove_wrapping (WebKitDOMElement *element)
+{
+       WebKitDOMNodeList *list;
+       gint ii, length;
+
+       list = webkit_dom_element_query_selector_all (
+               element, "br.-x-evo-wrap-br", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++)
+               remove_node (webkit_dom_node_list_item (list, ii));
+}
+
+static void
+remove_quoting (WebKitDOMElement *element)
+{
+       WebKitDOMNodeList *list;
+       gint ii, length;
+
+       list = webkit_dom_element_query_selector_all (
+               element, "span.-x-evo-quoted", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               remove_node (webkit_dom_node_list_item (list, ii));
+       }
+
+       list = webkit_dom_element_query_selector_all (
+               element, "span.-x-evo-temp-text-wrapper", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *nd = webkit_dom_node_list_item (list, ii);
+
+               while (webkit_dom_node_has_child_nodes (nd)) {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (nd),
+                               webkit_dom_node_get_first_child (nd),
+                               nd,
+                               NULL);
+               }
+
+               remove_node (nd);
+       }
+
+       webkit_dom_node_normalize (WEBKIT_DOM_NODE (element));
+}
+
+static gboolean
+node_is_list (WebKitDOMNode *node)
+{
+       return node && (
+               WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (node) ||
+               WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (node));
+}
+
+static gint
+get_citation_level (WebKitDOMNode *node)
+{
+       WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+       gint level = 0;
+
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
+                   webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type"))
+                       level++;
+
+               parent = webkit_dom_node_get_parent_node (parent);
+       }
+
+       return level;
+}
+
+static void
+format_change_block_to_block (EHTMLEditorSelection *selection,
+                              EHTMLEditorSelectionBlockFormat format,
+                              EHTMLEditorView *view,
+                              const gchar *value,
+                              WebKitDOMDocument *document)
+{
+       gboolean after_selection_end, quoted = FALSE;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker, *element;
+       WebKitDOMNode *block, *next_block;
+
+       e_html_editor_selection_save (selection);
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+       block = webkit_dom_node_get_parent_node (
+               WEBKIT_DOM_NODE (selection_start_marker));
+       if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-temp-text-wrapper")) {
+               block = webkit_dom_node_get_parent_node (block);
+               remove_wrapping (WEBKIT_DOM_ELEMENT (block));
+               remove_quoting (WEBKIT_DOM_ELEMENT (block));
+               quoted = TRUE;
+       }
+
+       /* Process all blocks that are in the selection one by one */
+       while (block) {
+               WebKitDOMNode *child;
+
+               after_selection_end = webkit_dom_node_contains (
+                       block, WEBKIT_DOM_NODE (selection_end_marker));
+
+               next_block = webkit_dom_node_get_next_sibling (
+                       WEBKIT_DOM_NODE (block));
+
+               if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH)
+                       element = e_html_editor_selection_get_paragraph_element (
+                               selection, document, -1, 0);
+               else
+                       element = webkit_dom_document_create_element (
+                               document, value, NULL);
+
+               while ((child = webkit_dom_node_get_first_child (block)))
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (element), child, NULL);
+
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (block),
+                       WEBKIT_DOM_NODE (element),
+                       block,
+                       NULL);
+
+               remove_node (block);
+
+               block = next_block;
+
+               if (after_selection_end)
+                       break;
+       }
+
+       if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH &&
+           !e_html_editor_view_get_html_mode (view)) {
+               gint citation_level, quote;
+
+               citation_level = get_citation_level (WEBKIT_DOM_NODE (element));
+               quote = citation_level ? citation_level + 1 : 0;
+
+               element = e_html_editor_selection_wrap_paragraph_length (
+                       selection, element, selection->priv->word_wrap_length - quote);
+       }
+
+       if (quoted)
+               e_html_editor_view_quote_plain_text_element (view, element);
+
+       e_html_editor_selection_restore (selection);
+}
+
+static void
+remove_node_if_empty (WebKitDOMNode *node)
+{
+       if (!WEBKIT_DOM_IS_NODE (node))
+               return;
+
+       if (!webkit_dom_node_get_first_child (node)) {
+               remove_node (node);
+       } else {
+               gchar *text_content;
+
+               text_content = webkit_dom_node_get_text_content (node);
+               if (!text_content)
+                       remove_node (node);
+
+               if (text_content && !*text_content)
+                       remove_node (node);
+
+               g_free (text_content);
+       }
+}
+
+static void
+format_change_block_to_list (EHTMLEditorSelection *selection,
+                             EHTMLEditorSelectionBlockFormat format,
+                             EHTMLEditorView *view,
+                             WebKitDOMDocument *document)
+{
+       gboolean after_selection_end;
+       gboolean html_mode = e_html_editor_view_get_html_mode (view);
+       WebKitDOMElement *selection_start_marker, *selection_end_marker, *item, *list;
+       WebKitDOMNode *block, *next_block;
+
+       e_html_editor_selection_save (selection);
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+       block = webkit_dom_node_get_parent_node (
+               WEBKIT_DOM_NODE (selection_start_marker));
+
+       list = create_list_element (selection, document, format, 0, html_mode);
+
+       if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-temp-text-wrapper")) {
+               WebKitDOMElement *element;
+
+               block = webkit_dom_node_get_parent_node (block);
+
+               remove_wrapping (WEBKIT_DOM_ELEMENT (block));
+               remove_quoting (WEBKIT_DOM_ELEMENT (block));
+
+               e_html_editor_view_exec_command (
+                       view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL);
+
+               element = webkit_dom_document_query_selector (
+                       document, "body>br", NULL);
+
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       WEBKIT_DOM_NODE (list),
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+
+               selection_start_marker = webkit_dom_document_query_selector (
+                       document, "span#-x-evo-selection-start-marker", NULL);
+               selection_end_marker = webkit_dom_document_query_selector (
+                       document, "span#-x-evo-selection-end-marker", NULL);
+               block = webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+       } else
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (block),
+                       WEBKIT_DOM_NODE (list),
+                       block,
+                       NULL);
+
+       /* Process all blocks that are in the selection one by one */
+       while (block) {
+               gboolean empty = FALSE;
+               gchar *content;
+               WebKitDOMNode *child;
+
+               after_selection_end = webkit_dom_node_contains (
+                       block, WEBKIT_DOM_NODE (selection_end_marker));
+
+               next_block = webkit_dom_node_get_next_sibling (
+                       WEBKIT_DOM_NODE (block));
+
+               item = webkit_dom_document_create_element (document, "LI", NULL);
+               content = webkit_dom_node_get_text_content (block);
+
+               empty = !*content || (g_strcmp0 (content, UNICODE_ZERO_WIDTH_SPACE) == 0);
+               g_free (content);
+
+               /* We have to use again the hidden space to move caret into newly inserted list */
+               if (empty) {
+                       webkit_dom_html_element_set_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (item),
+                               UNICODE_ZERO_WIDTH_SPACE,
+                               NULL);
+               }
+
+               while ((child = webkit_dom_node_get_first_child (block)))
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (item), child, NULL);
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (list), WEBKIT_DOM_NODE (item), NULL);
+
+               remove_node (block);
+
+               block = next_block;
+
+               if (after_selection_end)
+                       break;
+       }
+
+       merge_lists_if_possible (WEBKIT_DOM_NODE (list));
+
+       e_html_editor_view_force_spell_check (view);
+       e_html_editor_selection_restore (selection);
+}
+
+static void
+format_change_list_to_list (EHTMLEditorSelection *selection,
+                            EHTMLEditorSelectionBlockFormat format,
+                            WebKitDOMDocument *document,
+                            gboolean html_mode)
+{
+       EHTMLEditorSelectionBlockFormat prev = 0, next = 0;
+       gboolean done = FALSE, indented = FALSE;
+       gboolean selection_starts_in_first_child, selection_ends_in_last_child;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *prev_list, *current_list, *next_list;
+
+       e_html_editor_selection_save (selection);
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       current_list = webkit_dom_node_get_parent_node (
+               webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (selection_start_marker)));
+
+       prev_list = webkit_dom_node_get_parent_node (
+               webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (selection_start_marker)));
+
+       next_list = webkit_dom_node_get_parent_node (
+               webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (selection_end_marker)));
+
+       selection_starts_in_first_child =
+               webkit_dom_node_contains (
+                       webkit_dom_node_get_first_child (current_list),
+                       WEBKIT_DOM_NODE (selection_start_marker));
+
+       selection_ends_in_last_child =
+               webkit_dom_node_contains (
+                       webkit_dom_node_get_last_child (current_list),
+                       WEBKIT_DOM_NODE (selection_end_marker));
+
+       indented = element_has_class (WEBKIT_DOM_ELEMENT (current_list), "-x-evo-indented");
+
+       if (!prev_list || !next_list || indented) {
+               format_change_list_from_list (selection, document, format, html_mode);
+               goto out;
+       }
+
+       if (webkit_dom_node_is_same_node (prev_list, next_list)) {
+               prev_list = webkit_dom_node_get_previous_sibling (
+                       webkit_dom_node_get_parent_node (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (selection_start_marker))));
+               next_list = webkit_dom_node_get_next_sibling (
+                       webkit_dom_node_get_parent_node (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (selection_end_marker))));
+               if (!prev_list || !next_list) {
+                       format_change_list_from_list (selection, document, format, html_mode);
+                       goto out;
+               }
+       }
+
+       prev = e_html_editor_selection_get_list_format_from_node (prev_list);
+       next = e_html_editor_selection_get_list_format_from_node (next_list);
+
+       if (format == prev && format != -1 && prev != -1) {
+               if (selection_starts_in_first_child && selection_ends_in_last_child) {
+                       done = TRUE;
+                       merge_list_into_list (current_list, prev_list, FALSE);
+               }
+       }
+
+       if (format == next && format != -1 && next != -1) {
+               if (selection_starts_in_first_child && selection_ends_in_last_child) {
+                       done = TRUE;
+                       merge_list_into_list (next_list, prev_list, FALSE);
+               }
+       }
+
+       if (done)
+               goto out;
+
+       format_change_list_from_list (selection, document, format, html_mode);
+out:
+       e_html_editor_selection_restore (selection);
+}
+
+static void
+format_change_list_to_block (EHTMLEditorSelection *selection,
+                             EHTMLEditorSelectionBlockFormat format,
+                             WebKitDOMDocument *document)
+{
+       gboolean after_end = FALSE;
+       WebKitDOMElement *selection_start, *element, *selection_end;
+       WebKitDOMNode *source_list, *next_item, *item, *source_list_clone;
+
+       e_html_editor_selection_save (selection);
+
+       selection_start = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       item = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start));
+       source_list = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (item));
+       source_list_clone = webkit_dom_node_clone_node (source_list, FALSE);
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (source_list),
+               WEBKIT_DOM_NODE (source_list_clone),
+               webkit_dom_node_get_next_sibling (source_list),
+               NULL);
+
+       next_item = item;
+
+       /* Process all nodes that are in selection one by one */
+       while (next_item) {
+               WebKitDOMNode *tmp;
+
+               tmp = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (next_item));
+
+               if (!after_end) {
+                       if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH)
+                               element = e_html_editor_selection_get_paragraph_element (selection, document, 
-1, 0);
+                       else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE)
+                               element = webkit_dom_document_create_element (document, "PRE", NULL);
+                       else if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE)
+                               element = webkit_dom_document_create_element (document, "BLOCKQUOTE", NULL);
+                       else
+                               element = e_html_editor_selection_get_paragraph_element (selection, document, 
-1, 0);
+
+                       after_end = webkit_dom_node_contains (next_item, WEBKIT_DOM_NODE (selection_end));
+
+                       while (webkit_dom_node_get_first_child (next_item)) {
+                               WebKitDOMNode *node = webkit_dom_node_get_first_child (next_item);
+
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (element), node, NULL);
+                       }
+
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (source_list),
+                               WEBKIT_DOM_NODE (element),
+                               source_list_clone,
+                               NULL);
+
+                       remove_node (next_item);
+
+                       next_item = tmp;
+               } else {
+                       webkit_dom_node_append_child (
+                               source_list_clone, next_item, NULL);
+
+                       next_item = tmp;
+               }
+       }
+
+       remove_node_if_empty (source_list_clone);
+       remove_node_if_empty (source_list);
+
+       e_html_editor_selection_restore (selection);
+}
+
+/**
+ * e_html_editor_selection_set_block_format:
+ * @selection: an #EHTMLEditorSelection
+ * @format: an #EHTMLEditorSelectionBlockFormat value
+ *
+ * Changes block format of current paragraph to @format.
+ */
+void
+e_html_editor_selection_set_block_format (EHTMLEditorSelection *selection,
+                                          EHTMLEditorSelectionBlockFormat format)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorSelectionBlockFormat current_format;
+       const gchar *value;
+       gboolean has_selection = FALSE;
+       gboolean from_list = FALSE, to_list = FALSE, html_mode;
+       WebKitDOMDocument *document;
+       WebKitDOMRange *range;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       current_format = e_html_editor_selection_get_block_format (selection);
+       if (current_format == format) {
+               return;
+       }
+
+       switch (format) {
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE:
+                       value = "BLOCKQUOTE";
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1:
+                       value = "H1";
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2:
+                       value = "H2";
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3:
+                       value = "H3";
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4:
+                       value = "H4";
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5:
+                       value = "H5";
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6:
+                       value = "H6";
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH:
+                       value = "P";
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE:
+                       value = "PRE";
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS:
+                       value = "ADDRESS";
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST:
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA:
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN:
+                       to_list = TRUE;
+                       value = NULL;
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST:
+                       to_list = TRUE;
+                       value = NULL;
+                       break;
+               case E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE:
+               default:
+                       value = NULL;
+                       break;
+       }
+
+       if (g_strcmp0 (e_html_editor_selection_get_string (selection), "") != 0)
+               has_selection = TRUE;
+
+       /* H1 - H6 have bold font by default */
+       if (format >= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1 &&
+           format <= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6)
+               selection->priv->is_bold = TRUE;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       html_mode = e_html_editor_view_get_html_mode (view);
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       from_list =
+               current_format >= E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST;
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range) {
+               g_object_unref (view);
+               return;
+       }
+
+       if (from_list && to_list)
+               format_change_list_to_list (selection, format, document, html_mode);
+
+       if (!from_list && !to_list)
+               format_change_block_to_block (selection, format, view, value, document);
+
+       if (from_list && !to_list)
+               format_change_list_to_block (selection, format, document);
+
+       if (!from_list && to_list)
+               format_change_block_to_list (selection, format, view, document);
+
+       if (!has_selection)
+               e_html_editor_view_force_spell_check (view);
+
+       g_object_unref (view);
+
+       /* When changing the format we need to re-set the alignment */
+       e_html_editor_selection_set_alignment (selection, selection->priv->alignment);
+
+       g_object_notify (G_OBJECT (selection), "block-format");
+}
+
+/**
+ * e_html_editor_selection_get_font_color:
+ * @selection: an #EHTMLEditorSelection
+ * @rgba: a #GdkRGBA object to be set to current font color
+ *
+ * Sets @rgba to contain color of current text selection or letter at current
+ * cursor position.
+ */
+void
+e_html_editor_selection_get_font_color (EHTMLEditorSelection *selection,
+                                        GdkRGBA *rgba)
+{
+       gchar *color;
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       if (g_strcmp0 (e_html_editor_selection_get_string (selection), "") == 0) {
+               color = g_strdup (selection->priv->font_color);
+       } else {
+               color = get_font_property (selection, "color");
+               if (!color) {
+                       *rgba = black;
+                       return;
+               }
+       }
+
+       gdk_rgba_parse (rgba, color);
+       g_free (color);
+}
+
+/**
+ * e_html_editor_selection_set_font_color:
+ * @selection: an #EHTMLEditorSelection
+ * @rgba: a #GdkRGBA
+ *
+ * Sets font color of current selection or letter at current cursor position to
+ * color defined in @rgba.
+ */
+void
+e_html_editor_selection_set_font_color (EHTMLEditorSelection *selection,
+                                        const GdkRGBA *rgba)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+       guint32 rgba_value;
+       gchar *color;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       if (!rgba)
+               rgba = &black;
+
+       rgba_value = e_rgba_to_value ((GdkRGBA *) rgba);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR;
+       color = g_strdup_printf ("#%06x", rgba_value);
+       selection->priv->font_color = g_strdup (color);
+       e_html_editor_view_exec_command (view, command, color);
+       g_free (color);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "font-color");
+}
+
+/**
+ * e_html_editor_selection_get_font_name:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns name of font used in current selection or at letter at current cursor
+ * position.
+ *
+ * Returns: A string with font name. [transfer-none]
+ */
+const gchar *
+e_html_editor_selection_get_font_name (EHTMLEditorSelection *selection)
+{
+       WebKitDOMNode *node;
+       WebKitDOMRange *range;
+       WebKitDOMCSSStyleDeclaration *css;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+       range = html_editor_selection_get_current_range (selection);
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+       g_free (selection->priv->font_family);
+       css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node));
+       selection->priv->font_family =
+               webkit_dom_css_style_declaration_get_property_value (css, "fontFamily");
+
+       return selection->priv->font_family;
+}
+
+/**
+ * e_html_editor_selection_set_font_name:
+ * @selection: an #EHTMLEditorSelection
+ * @font_name: a font name to apply
+ *
+ * Sets font name of current selection or of letter at current cursor position
+ * to @font_name.
+ */
+void
+e_html_editor_selection_set_font_name (EHTMLEditorSelection *selection,
+                                       const gchar *font_name)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME;
+       e_html_editor_view_exec_command (view, command, font_name);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "font-name");
+}
+
+/**
+ * e_editor_Selection_get_font_size:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns point size of current selection or of letter at current cursor position.
+ */
+ guint
+e_html_editor_selection_get_font_size (EHTMLEditorSelection *selection)
+{
+       gchar *size;
+       guint size_int;
+
+       g_return_val_if_fail (
+               E_IS_HTML_EDITOR_SELECTION (selection),
+               E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL);
+
+       size = get_font_property (selection, "size");
+       if (!size)
+               return E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL;
+
+       size_int = atoi (size);
+       g_free (size);
+
+       if (size_int == 0)
+               return E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL;
+
+       return size_int;
+}
+
+/**
+ * e_html_editor_selection_set_font_size:
+ * @selection: an #EHTMLEditorSelection
+ * @font_size: point size to apply
+ *
+ * Sets font size of current selection or of letter at current cursor position
+ * to @font_size.
+ */
+void
+e_html_editor_selection_set_font_size (EHTMLEditorSelection *selection,
+                                       guint font_size)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+       gchar *size_str;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       selection->priv->font_size = font_size;
+       command = E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE;
+       size_str = g_strdup_printf ("%d", font_size);
+       e_html_editor_view_exec_command (view, command, size_str);
+       g_free (size_str);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "font-size");
+}
+
+/**
+ * e_html_editor_selection_is_citation:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current paragraph is a citation.
+ *
+ * Returns: @TRUE when current paragraph is a citation, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_citation (EHTMLEditorSelection *selection)
+{
+       gboolean ret_val;
+       gchar *value, *text_content;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return FALSE;
+
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+       if (WEBKIT_DOM_IS_TEXT (node))
+               return get_has_style (selection, "citation");
+
+       /* If we are changing the format of block we have to re-set bold property,
+        * otherwise it will be turned off because of no text in composer */
+       text_content = webkit_dom_node_get_text_content (node);
+       if (g_strcmp0 (text_content, "") == 0) {
+               g_free (text_content);
+               return FALSE;
+       }
+       g_free (text_content);
+
+       value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
+
+       /* citation == <blockquote type='cite'> */
+       if (g_strstr_len (value, -1, "cite"))
+               ret_val = TRUE;
+       else
+               ret_val = get_has_style (selection, "citation");
+
+       g_free (value);
+       return ret_val;
+}
+
+/**
+ * e_html_editor_selection_is_indented:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current paragraph is indented. This does not include
+ * citations.  To check, whether paragraph is a citation, use
+ * e_html_editor_selection_is_citation().
+ *
+ * Returns: @TRUE when current paragraph is indented, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_indented (EHTMLEditorSelection *selection)
+{
+       WebKitDOMRange *range;
+       WebKitDOMNode *node;
+       WebKitDOMElement *element;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return FALSE;
+
+       node = webkit_dom_range_get_end_container (range, NULL);
+       if (!WEBKIT_DOM_IS_ELEMENT (node))
+               node = WEBKIT_DOM_NODE (webkit_dom_node_get_parent_element (node));
+
+       element = webkit_dom_node_get_parent_element (node);
+
+       return element_has_class (element, "-x-evo-indented");
+}
+
+static gboolean
+is_in_html_mode (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view = e_html_editor_selection_ref_html_editor_view (selection);
+       gboolean ret_val;
+
+       g_return_val_if_fail (view != NULL, FALSE);
+
+       ret_val = e_html_editor_view_get_html_mode (view);
+
+       g_object_unref (view);
+
+       return ret_val;
+}
+
+static gint
+get_list_level (WebKitDOMNode *node)
+{
+       gint level = 0;
+
+       while (node && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) {
+               if (node_is_list (node))
+                       level++;
+               node = webkit_dom_node_get_parent_node (node);
+       }
+
+       return level;
+}
+
+/**
+ * e_html_editor_selection_indent:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Indents current paragraph by one level.
+ */
+void
+e_html_editor_selection_indent (EHTMLEditorSelection *selection)
+{
+       gboolean has_selection;
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       has_selection = g_strcmp0 (e_html_editor_selection_get_string (selection), "") != 0;
+
+       if (!has_selection) {
+               WebKitDOMDocument *document;
+               WebKitDOMRange *range;
+               WebKitDOMNode *node, *clone;
+               WebKitDOMElement *element, *caret_position;
+               gint word_wrap_length = selection->priv->word_wrap_length;
+               gint level;
+               gint final_width = 0;
+
+               document = webkit_web_view_get_dom_document (
+                       WEBKIT_WEB_VIEW (view));
+
+               caret_position = e_html_editor_selection_save_caret_position (selection);
+
+               range = html_editor_selection_get_current_range (selection);
+               if (!range) {
+                       g_object_unref (view);
+                       return;
+               }
+
+               node = webkit_dom_range_get_end_container (range, NULL);
+               if (!WEBKIT_DOM_IS_ELEMENT (node))
+                       node = WEBKIT_DOM_NODE (
+                               webkit_dom_node_get_parent_element (node));
+
+               if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node)) {
+                       gboolean html_mode = e_html_editor_view_get_html_mode (view);
+                       WebKitDOMElement *list;
+                       WebKitDOMNode *source_list = webkit_dom_node_get_parent_node (node);
+                       EHTMLEditorSelectionBlockFormat format;
+
+                       format = e_html_editor_selection_get_list_format_from_node (source_list);
+
+                       list = create_list_element (
+                               selection, document, format, get_list_level (node), html_mode);
+
+                       element_add_class (list, "-x-evo-indented");
+                       webkit_dom_node_insert_before (
+                               source_list, WEBKIT_DOM_NODE (list), node, NULL);
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (list), node, NULL);
+                       if (!webkit_dom_node_contains (node, WEBKIT_DOM_NODE (caret_position))) {
+                               webkit_dom_node_append_child (
+                                       node, WEBKIT_DOM_NODE (caret_position), NULL);
+                       }
+
+                       merge_lists_if_possible (WEBKIT_DOM_NODE (list));
+
+                       e_html_editor_selection_restore_caret_position (selection);
+
+                       g_object_unref (view);
+                       return;
+               }
+
+               level = get_indentation_level (WEBKIT_DOM_ELEMENT (node));
+
+               final_width = word_wrap_length - SPACES_PER_INDENTATION * (level + 1);
+               if (final_width < 10 && !is_in_html_mode (selection)) {
+                       e_html_editor_selection_restore_caret_position (selection);
+                       g_object_unref (view);
+                       return;
+               }
+
+               element = webkit_dom_node_get_parent_element (node);
+               clone = webkit_dom_node_clone_node (node, TRUE);
+
+               /* Remove style and let the paragraph inherit it from parent */
+               if (element_has_class (WEBKIT_DOM_ELEMENT (clone), "-x-evo-paragraph"))
+                       webkit_dom_element_remove_attribute (
+                               WEBKIT_DOM_ELEMENT (clone), "style");
+
+               element = e_html_editor_selection_get_indented_element (
+                       selection, document, final_width);
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (element),
+                       clone,
+                       NULL);
+
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (element),
+                       node,
+                       NULL);
+
+               e_html_editor_selection_restore_caret_position (selection);
+       } else {
+               command = E_HTML_EDITOR_VIEW_COMMAND_INDENT;
+               e_html_editor_selection_save (selection);
+               e_html_editor_view_exec_command (view, command, NULL);
+       }
+
+       e_html_editor_view_force_spell_check_for_current_paragraph (view);
+
+       if (has_selection)
+               e_html_editor_selection_restore (selection);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "indented");
+}
+
+static const gchar *
+get_css_alignment_value (EHTMLEditorSelectionAlignment alignment)
+{
+       if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT)
+               return ""; /* Left is by default on ltr */
+
+       if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER)
+               return  "text-align: center;";
+
+       if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT)
+               return "text-align: right;";
+
+       return "";
+}
+
+static void
+unindent_list (EHTMLEditorSelection *selection,
+               WebKitDOMDocument *document)
+{
+       gboolean after = FALSE;
+       WebKitDOMElement *new_list;
+       WebKitDOMNode *source_list, *source_list_clone, *current_list, *item;
+       WebKitDOMElement *selection_start_marker;
+       WebKitDOMElement *selection_end_marker;
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       if (!selection_start_marker || !selection_end_marker)
+               return;
+
+       /* Copy elements from previous block to list */
+       item = webkit_dom_node_get_parent_node (
+               WEBKIT_DOM_NODE (selection_start_marker));
+       source_list = webkit_dom_node_get_parent_node (item);
+       new_list = WEBKIT_DOM_ELEMENT (
+               webkit_dom_node_clone_node (source_list, FALSE));
+       current_list = source_list;
+       source_list_clone = webkit_dom_node_clone_node (source_list, FALSE);
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (source_list),
+               WEBKIT_DOM_NODE (source_list_clone),
+               webkit_dom_node_get_next_sibling (source_list),
+               NULL);
+
+       if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented"))
+               element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented");
+
+       while (item) {
+               WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item);
+
+               if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
+                       if (after) {
+                               webkit_dom_node_append_child (
+                                       source_list_clone, WEBKIT_DOM_NODE (item), NULL);
+                       } else {
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (source_list),
+                                       item,
+                                       webkit_dom_node_get_next_sibling (source_list),
+                                       NULL);
+                       }
+               }
+
+               if (webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end_marker)))
+                       after = TRUE;
+
+               if (!next_item) {
+                       if (after)
+                               break;
+
+                       current_list = webkit_dom_node_get_next_sibling (current_list);
+                       next_item = webkit_dom_node_get_first_child (current_list);
+               }
+               item = next_item;
+       }
+
+       remove_node_if_empty (source_list_clone);
+       remove_node_if_empty (source_list);
+}
+/**
+ * e_html_editor_selection_unindent:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Unindents current paragraph by one level.
+ */
+void
+e_html_editor_selection_unindent (EHTMLEditorSelection *selection)
+{
+       gboolean has_selection;
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       has_selection = g_strcmp0 (e_html_editor_selection_get_string (selection), "") != 0;
+
+       if (!has_selection) {
+               EHTMLEditorSelectionAlignment alignment;
+               gboolean before_node = TRUE, reinsert_caret_position = FALSE;
+               const gchar *align_value;
+               gint word_wrap_length = selection->priv->word_wrap_length;
+               gint level, width;
+               WebKitDOMDocument *document;
+               WebKitDOMElement *element;
+               WebKitDOMElement *prev_blockquote = NULL, *next_blockquote = NULL;
+               WebKitDOMNode *node, *clone, *node_clone, *caret_node;
+               WebKitDOMRange *range;
+
+               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+               e_html_editor_selection_save_caret_position (selection);
+
+               alignment = e_html_editor_selection_get_alignment (selection);
+               align_value = get_css_alignment_value (alignment);
+
+               range = html_editor_selection_get_current_range (selection);
+               if (!range) {
+                       g_object_unref (view);
+                       return;
+               }
+
+               node = webkit_dom_range_get_end_container (range, NULL);
+               if (!WEBKIT_DOM_IS_ELEMENT (node))
+                       node = WEBKIT_DOM_NODE (webkit_dom_node_get_parent_element (node));
+
+               element = webkit_dom_node_get_parent_element (node);
+
+               if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node)) {
+                       e_html_editor_selection_save (selection);
+                       e_html_editor_selection_clear_caret_position_marker (selection);
+
+                       unindent_list (selection, document);
+                       e_html_editor_selection_restore (selection);
+                       g_object_unref (view);
+                       return;
+               }
+
+               if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element))
+                       return;
+
+               element_add_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-to-unindent");
+
+               level = get_indentation_level (element);
+               width = word_wrap_length - SPACES_PER_INDENTATION * level;
+               clone = WEBKIT_DOM_NODE (webkit_dom_node_clone_node (WEBKIT_DOM_NODE (element), TRUE));
+
+               /* Look if we have previous siblings, if so, we have to
+                * create new blockquote that will include them */
+               if (webkit_dom_node_get_previous_sibling (node))
+                       prev_blockquote = e_html_editor_selection_get_indented_element (
+                               selection, document, width);
+
+               /* Look if we have next siblings, if so, we have to
+                * create new blockquote that will include them */
+               if (webkit_dom_node_get_next_sibling (node))
+                       next_blockquote = e_html_editor_selection_get_indented_element (
+                               selection, document, width);
+
+               /* Copy nodes that are before / after the element that we want to unindent */
+               while (webkit_dom_node_has_child_nodes (clone)) {
+                       WebKitDOMNode *child;
+
+                       child = webkit_dom_node_get_first_child (clone);
+
+                       if (is_caret_position_node (child)) {
+                               reinsert_caret_position = TRUE;
+                               caret_node = webkit_dom_node_clone_node (child, TRUE);
+                               remove_node (child);
+                               continue;
+                       }
+
+                       if (webkit_dom_node_is_equal_node (child, node)) {
+                               before_node = FALSE;
+                               node_clone = webkit_dom_node_clone_node (child, TRUE);
+                               remove_node (child);
+                               continue;
+                       }
+
+                       webkit_dom_node_append_child (
+                               before_node ?
+                                       WEBKIT_DOM_NODE (prev_blockquote) :
+                                       WEBKIT_DOM_NODE (next_blockquote),
+                               child,
+                               NULL);
+
+                       remove_node (child);
+               }
+
+               element_remove_class (WEBKIT_DOM_ELEMENT (node_clone), "-x-evo-to-unindent");
+
+               /* Insert blockqoute with nodes that were before the element that we want to unindent */
+               if (prev_blockquote) {
+                       if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (prev_blockquote))) {
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                                       WEBKIT_DOM_NODE (prev_blockquote),
+                                       WEBKIT_DOM_NODE (element),
+                                       NULL);
+                       }
+               }
+
+               /* Reinsert the caret position */
+               if (reinsert_caret_position) {
+                       webkit_dom_node_insert_before (
+                               node_clone,
+                               caret_node,
+                               webkit_dom_node_get_first_child (node_clone),
+                               NULL);
+               }
+
+               if (level == 1 && element_has_class (WEBKIT_DOM_ELEMENT (node_clone), "-x-evo-paragraph"))
+                       e_html_editor_selection_set_paragraph_style (
+                               selection, WEBKIT_DOM_ELEMENT (node_clone), word_wrap_length, 0, align_value);
+
+               /* Insert the unindented element */
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       node_clone,
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+
+               /* Insert blockqoute with nodes that were after the element that we want to unindent */
+               if (next_blockquote) {
+                       if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (prev_blockquote))) {
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                                       WEBKIT_DOM_NODE (next_blockquote),
+                                       WEBKIT_DOM_NODE (element),
+                                       NULL);
+                       }
+               }
+
+               /* Remove old blockquote */
+               remove_node (WEBKIT_DOM_NODE (element));
+
+               e_html_editor_selection_restore_caret_position (selection);
+       } else {
+               command = E_HTML_EDITOR_VIEW_COMMAND_OUTDENT;
+               e_html_editor_selection_save (selection);
+               e_html_editor_view_exec_command (view, command, NULL);
+       }
+
+       e_html_editor_view_force_spell_check_for_current_paragraph (view);
+
+       if (has_selection)
+               e_html_editor_selection_restore (selection);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "indented");
+}
+
+/**
+ * e_html_editor_selection_is_bold:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is bold.
+ *
+ * Returns @TRUE when selection is bold, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_bold (EHTMLEditorSelection *selection)
+{
+       gboolean ret_val;
+       gchar *value, *text_content;
+       EHTMLEditorView *view;
+       WebKitDOMCSSStyleDeclaration *style;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMNode *node;
+       WebKitDOMElement *element;
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_val_if_fail (view != NULL, FALSE);
+
+       if (!e_html_editor_view_get_html_mode (view)) {
+               g_object_unref (view);
+               return FALSE;
+       }
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       g_object_unref (view);
+       window = webkit_dom_document_get_default_view (document);
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return FALSE;
+
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+       /* If we are changing the format of block we have to re-set bold property,
+        * otherwise it will be turned off because of no text in composer */
+       text_content = webkit_dom_node_get_text_content (node);
+       if (g_strcmp0 (text_content, "") == 0) {
+               g_free (text_content);
+               return selection->priv->is_bold;
+       }
+       g_free (text_content);
+
+       if (WEBKIT_DOM_IS_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = webkit_dom_node_get_parent_element (node);
+
+       style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+       value = webkit_dom_css_style_declaration_get_property_value (style, "font-weight");
+
+       if (g_strstr_len (value, -1, "normal"))
+               ret_val = FALSE;
+       else
+               ret_val = TRUE;
+
+       g_free (value);
+       return ret_val;
+}
+
+/**
+ * e_html_editor_selection_set_bold:
+ * @selection: an #EHTMLEditorSelection
+ * @bold: @TRUE to enable bold, @FALSE to disable
+ *
+ * Toggles bold formatting of current selection or letter at current cursor
+ * position, depending on whether @bold is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_bold (EHTMLEditorSelection *selection,
+                                  gboolean bold)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       if (e_html_editor_selection_is_bold (selection) == bold)
+               return;
+
+       selection->priv->is_bold = bold;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_BOLD;
+       e_html_editor_view_exec_command (view, command, NULL);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "bold");
+}
+
+/**
+ * e_html_editor_selection_is_italic:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is italic.
+ *
+ * Returns @TRUE when selection is italic, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_italic (EHTMLEditorSelection *selection)
+{
+       gboolean ret_val;
+       gchar *value, *text_content;
+       EHTMLEditorView *view;
+       WebKitDOMCSSStyleDeclaration *style;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMNode *node;
+       WebKitDOMElement *element;
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_val_if_fail (view != NULL, FALSE);
+
+       if (!e_html_editor_view_get_html_mode (view)) {
+               g_object_unref (view);
+               return FALSE;
+       }
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       g_object_unref (view);
+       window = webkit_dom_document_get_default_view (document);
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return FALSE;
+
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+       /* If we are changing the format of block we have to re-set italic property,
+        * otherwise it will be turned off because of no text in composer */
+       text_content = webkit_dom_node_get_text_content (node);
+       if (g_strcmp0 (text_content, "") == 0) {
+               g_free (text_content);
+               return selection->priv->is_italic;
+       }
+       g_free (text_content);
+
+       if (WEBKIT_DOM_IS_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = webkit_dom_node_get_parent_element (node);
+
+       style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+       value = webkit_dom_css_style_declaration_get_property_value (style, "font-style");
+
+       if (g_strstr_len (value, -1, "italic"))
+               ret_val = TRUE;
+       else
+               ret_val = FALSE;
+
+       g_free (value);
+       return ret_val;
+}
+
+/**
+ * e_html_editor_selection_set_italic:
+ * @selection: an #EHTMLEditorSelection
+ * @italic: @TRUE to enable italic, @FALSE to disable
+ *
+ * Toggles italic formatting of current selection or letter at current cursor
+ * position, depending on whether @italic is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_italic (EHTMLEditorSelection *selection,
+                                    gboolean italic)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       if (e_html_editor_selection_is_italic (selection) == italic)
+               return;
+
+       selection->priv->is_italic = italic;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_ITALIC;
+       e_html_editor_view_exec_command (view, command, NULL);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "italic");
+}
+
+static gboolean
+is_monospaced_element (WebKitDOMElement *element)
+{
+       gchar *value;
+       gboolean ret_val = FALSE;
+
+       if (!element)
+               return FALSE;
+
+       if (!WEBKIT_DOM_IS_HTML_FONT_ELEMENT (element))
+               return FALSE;
+
+       value = webkit_dom_element_get_attribute (element, "face");
+
+       if (g_strcmp0 (value, "monospace") == 0)
+               ret_val = TRUE;
+
+       g_free (value);
+
+       return ret_val;
+}
+
+/**
+ * e_html_editor_selection_is_monospaced:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is monospaced.
+ *
+ * Returns @TRUE when selection is monospaced, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_monospaced (EHTMLEditorSelection *selection)
+{
+       gboolean ret_val;
+       gchar *value, *text_content;
+       EHTMLEditorView *view;
+       WebKitDOMCSSStyleDeclaration *style;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMNode *node;
+       WebKitDOMElement *element;
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_val_if_fail (view != NULL, FALSE);
+
+       if (!e_html_editor_view_get_html_mode (view)) {
+               g_object_unref (view);
+               return FALSE;
+       }
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       g_object_unref (view);
+       window = webkit_dom_document_get_default_view (document);
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return FALSE;
+
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+       /* If we are changing the format of block we have to re-set italic property,
+        * otherwise it will be turned off because of no text in composer */
+       text_content = webkit_dom_node_get_text_content (node);
+       if (g_strcmp0 (text_content, "") == 0) {
+               g_free (text_content);
+               return selection->priv->is_monospaced;
+       }
+       g_free (text_content);
+
+       if (WEBKIT_DOM_IS_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = webkit_dom_node_get_parent_element (node);
+
+       style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+       value = webkit_dom_css_style_declaration_get_property_value (style, "font-family");
+
+       if (g_strstr_len (value, -1, "monospace"))
+               ret_val = TRUE;
+       else
+               ret_val = FALSE;
+
+       g_free (value);
+       return ret_val;
+}
+
+static void
+move_caret_into_element (WebKitDOMDocument *document,
+                        WebKitDOMElement *element)
+{
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *window_selection;
+       WebKitDOMRange *new_range;
+
+       if (!element)
+               return;
+
+       window = webkit_dom_document_get_default_view (document);
+       window_selection = webkit_dom_dom_window_get_selection (window);
+       new_range = webkit_dom_document_create_range (document);
+
+       webkit_dom_range_select_node_contents (
+                       new_range, WEBKIT_DOM_NODE (element), NULL);
+       webkit_dom_range_collapse (new_range, FALSE, NULL);
+       webkit_dom_dom_selection_remove_all_ranges (window_selection);
+       webkit_dom_dom_selection_add_range (window_selection, new_range);
+}
+
+/**
+ * e_html_editor_selection_set_monospaced:
+ * @selection: an #EHTMLEditorSelection
+ * @monospaced: @TRUE to enable monospaced, @FALSE to disable
+ *
+ * Toggles monospaced formatting of current selection or letter at current cursor
+ * position, depending on whether @monospaced is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_monospaced (EHTMLEditorSelection *selection,
+                                        gboolean monospaced)
+{
+       EHTMLEditorView *view;
+       WebKitWebView *web_view;
+       WebKitDOMDocument *document;
+       WebKitDOMRange *range;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *window_selection;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       if (e_html_editor_selection_is_monospaced (selection) == monospaced)
+               return;
+
+       selection->priv->is_monospaced = monospaced;
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       web_view = WEBKIT_WEB_VIEW (view);
+
+       document = webkit_web_view_get_dom_document (web_view);
+       window = webkit_dom_document_get_default_view (document);
+       window_selection = webkit_dom_dom_window_get_selection (window);
+
+       if (monospaced) {
+               gchar *font_size_str;
+               guint font_size;
+               WebKitDOMElement *monospace;
+
+               monospace = webkit_dom_document_create_element (
+                       document, "font", NULL);
+               webkit_dom_element_set_attribute (
+                       monospace, "face", "monospace", NULL);
+
+               font_size = selection->priv->font_size;
+               if (font_size == 0)
+                       font_size = E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL;
+               font_size_str = g_strdup_printf ("%d", font_size);
+               webkit_dom_element_set_attribute (
+                       monospace, "size", font_size_str, NULL);
+               g_free (font_size_str);
+
+               if (g_strcmp0 (e_html_editor_selection_get_string (selection), "") != 0) {
+                       gchar *html, *outer_html;
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (monospace),
+                               WEBKIT_DOM_NODE (
+                                       webkit_dom_range_clone_contents (range, NULL)),
+                               NULL);
+
+                       outer_html = webkit_dom_html_element_get_outer_html (
+                               WEBKIT_DOM_HTML_ELEMENT (monospace));
+
+                       html = g_strconcat (
+                               /* Mark selection for restoration */
+                               "<span id=\"-x-evo-selection-start-marker\"></span>",
+                               outer_html,
+                               "<span id=\"-x-evo-selection-end-marker\"></span>",
+                               NULL),
+
+                       e_html_editor_selection_insert_html (selection, html);
+
+                       e_html_editor_selection_restore (selection);
+
+                       g_free (html);
+                       g_free (outer_html);
+               } else {
+                       /* https://bugs.webkit.org/show_bug.cgi?id=15256 */
+                       webkit_dom_html_element_set_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (monospace),
+                               UNICODE_ZERO_WIDTH_SPACE,
+                               NULL);
+                       webkit_dom_range_insert_node (
+                               range, WEBKIT_DOM_NODE (monospace), NULL);
+
+                       move_caret_into_element (document, monospace);
+               }
+       } else {
+               gboolean is_bold, is_italic, is_underline, is_strikethrough;
+               guint font_size;
+               WebKitDOMElement *tt_element;
+               WebKitDOMNode *node;
+
+               node = webkit_dom_range_get_end_container (range, NULL);
+               if (WEBKIT_DOM_IS_ELEMENT (node) &&
+                   is_monospaced_element (WEBKIT_DOM_ELEMENT (node))) {
+                       tt_element = WEBKIT_DOM_ELEMENT (node);
+               } else {
+                       tt_element = e_html_editor_dom_node_find_parent_element (node, "FONT");
+
+                       if (!is_monospaced_element (tt_element)) {
+                               g_object_unref (view);
+                               return;
+                       }
+               }
+
+               /* Save current formatting */
+               is_bold = selection->priv->is_bold;
+               is_italic = selection->priv->is_italic;
+               is_underline = selection->priv->is_underline;
+               is_strikethrough = selection->priv->is_strikethrough;
+               font_size = selection->priv->font_size;
+               if (font_size == 0)
+                       font_size = E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL;
+
+               if (g_strcmp0 (e_html_editor_selection_get_string (selection), "") != 0) {
+                       gchar *html, *outer_html, *inner_html, *beginning, *end;
+                       gchar *start_position, *end_position, *font_size_str;
+                       WebKitDOMElement *wrapper;
+
+                       wrapper = webkit_dom_document_create_element (
+                               document, "SPAN", NULL);
+                       webkit_dom_element_set_id (wrapper, "-x-evo-remove-tt");
+                       webkit_dom_range_surround_contents (
+                               range, WEBKIT_DOM_NODE (wrapper), NULL);
+
+                       html = webkit_dom_html_element_get_outer_html (
+                               WEBKIT_DOM_HTML_ELEMENT (tt_element));
+
+                       start_position = g_strstr_len (
+                               html, -1, "<span id=\"-x-evo-remove-tt\"");
+                       end_position = g_strstr_len (start_position, -1, "</span>");
+
+                       beginning = g_utf8_substring (
+                               html, 0, g_utf8_pointer_to_offset (html, start_position));
+                       inner_html = webkit_dom_html_element_get_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (wrapper));
+                       end = g_utf8_substring (
+                               html,
+                               g_utf8_pointer_to_offset (html, end_position) + 7,
+                               g_utf8_strlen (html, -1)),
+
+                       font_size_str = g_strdup_printf ("%d", font_size);
+
+                       outer_html =
+                               g_strconcat (
+                                       /* Beginning */
+                                       beginning,
+                                       /* End the previous FONT tag */
+                                       "</font>",
+                                       /* Mark selection for restoration */
+                                       "<span id=\"-x-evo-selection-start-marker\"></span>",
+                                       /* Inside will be the same */
+                                       inner_html,
+                                       "<span id=\"-x-evo-selection-end-marker\"></span>",
+                                       /* Start the new FONT element */
+                                       "<font face=\"monospace\" size=\"",
+                                       font_size_str,
+                                       "\">",
+                                       /* End - we have to start after </span> */
+                                       end,
+                                       NULL),
+
+                       g_free (font_size_str);
+
+                       webkit_dom_html_element_set_outer_html (
+                               WEBKIT_DOM_HTML_ELEMENT (tt_element),
+                               outer_html,
+                               NULL);
+
+                       e_html_editor_selection_restore (selection);
+
+                       g_free (html);
+                       g_free (outer_html);
+                       g_free (inner_html);
+                       g_free (beginning);
+                       g_free (end);
+               } else {
+                       WebKitDOMRange *new_range;
+                       gchar *outer_html;
+                       gchar *tmp;
+
+                       webkit_dom_element_set_id (tt_element, "ev-tt");
+
+                       outer_html = webkit_dom_html_element_get_outer_html (
+                               WEBKIT_DOM_HTML_ELEMENT (tt_element));
+                       tmp = g_strconcat (outer_html, UNICODE_ZERO_WIDTH_SPACE, NULL);
+                       webkit_dom_html_element_set_outer_html (
+                               WEBKIT_DOM_HTML_ELEMENT (tt_element),
+                               tmp, NULL);
+
+                       /* We need to get that element again */
+                       tt_element = webkit_dom_document_get_element_by_id (
+                               document, "ev-tt");
+                       webkit_dom_element_remove_attribute (
+                               WEBKIT_DOM_ELEMENT (tt_element), "id");
+
+                       new_range = webkit_dom_document_create_range (document);
+                       webkit_dom_range_set_start_after (
+                               new_range, WEBKIT_DOM_NODE (tt_element), NULL);
+                       webkit_dom_range_set_end_after (
+                               new_range, WEBKIT_DOM_NODE (tt_element), NULL);
+
+                       webkit_dom_dom_selection_remove_all_ranges (
+                               window_selection);
+                       webkit_dom_dom_selection_add_range (
+                               window_selection, new_range);
+
+                       webkit_dom_dom_selection_modify (
+                               window_selection, "move", "right", "character");
+
+                       g_free (outer_html);
+                       g_free (tmp);
+
+                       e_html_editor_view_force_spell_check_for_current_paragraph (
+                               view);
+               }
+
+               /* Re-set formatting */
+               if (is_bold)
+                       e_html_editor_selection_set_bold (selection, TRUE);
+               if (is_italic)
+                       e_html_editor_selection_set_italic (selection, TRUE);
+               if (is_underline)
+                       e_html_editor_selection_set_underline (selection, TRUE);
+               if (is_strikethrough)
+                       e_html_editor_selection_set_strikethrough (selection, TRUE);
+
+               e_html_editor_selection_set_font_size (selection, font_size);
+       }
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "monospaced");
+}
+
+/**
+ * e_html_editor_selection_is_strikethrough:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is striked through.
+ *
+ * Returns @TRUE when selection is striked through, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_strikethrough (EHTMLEditorSelection *selection)
+{
+       gboolean ret_val;
+       gchar *value, *text_content;
+       EHTMLEditorView *view;
+       WebKitDOMCSSStyleDeclaration *style;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMNode *node;
+       WebKitDOMElement *element;
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_val_if_fail (view != NULL, FALSE);
+
+       if (!e_html_editor_view_get_html_mode (view)) {
+               g_object_unref (view);
+               return FALSE;
+       }
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       g_object_unref (view);
+       window = webkit_dom_document_get_default_view (document);
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return FALSE;
+
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+       /* If we are changing the format of block we have to re-set strikethrough property,
+        * otherwise it will be turned off because of no text in composer */
+       text_content = webkit_dom_node_get_text_content (node);
+       if (g_strcmp0 (text_content, "") == 0) {
+               g_free (text_content);
+               return selection->priv->is_strikethrough;
+       }
+       g_free (text_content);
+
+       if (WEBKIT_DOM_IS_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = webkit_dom_node_get_parent_element (node);
+
+       style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+       value = webkit_dom_css_style_declaration_get_property_value (style, "text-decoration");
+
+       if (g_strstr_len (value, -1, "line-through"))
+               ret_val = TRUE;
+       else
+               ret_val = get_has_style (selection, "strike");
+
+       g_free (value);
+       return ret_val;
+}
+
+/**
+ * e_html_editor_selection_set_strikethrough:
+ * @selection: an #EHTMLEditorSelection
+ * @strikethrough: @TRUE to enable strikethrough, @FALSE to disable
+ *
+ * Toggles strike through formatting of current selection or letter at current
+ * cursor position, depending on whether @strikethrough is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_strikethrough (EHTMLEditorSelection *selection,
+                                           gboolean strikethrough)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       if (e_html_editor_selection_is_strikethrough (selection) == strikethrough)
+               return;
+
+       selection->priv->is_strikethrough = strikethrough;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH;
+       e_html_editor_view_exec_command (view, command, NULL);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "strikethrough");
+}
+
+/**
+ * e_html_editor_selection_is_subscript:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is in subscript.
+ *
+ * Returns @TRUE when selection is in subscript, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_subscript (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_val_if_fail (view != NULL, FALSE);
+
+       if (!e_html_editor_view_get_html_mode (view)) {
+               g_object_unref (view);
+               return FALSE;
+       }
+
+       g_object_unref (view);
+
+       range = html_editor_selection_get_current_range (selection);
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+       while (node) {
+               gchar *tag_name;
+
+               tag_name = webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (node));
+
+               if (g_ascii_strncasecmp (tag_name, "sub", 3) == 0) {
+                       g_free (tag_name);
+                       break;
+               }
+
+               g_free (tag_name);
+               node = webkit_dom_node_get_parent_node (node);
+       }
+
+       return (node != NULL);
+}
+
+/**
+ * e_html_editor_selection_set_subscript:
+ * @selection: an #EHTMLEditorSelection
+ * @subscript: @TRUE to enable subscript, @FALSE to disable
+ *
+ * Toggles subscript of current selection or letter at current cursor position,
+ * depending on whether @subscript is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_subscript (EHTMLEditorSelection *selection,
+                                       gboolean subscript)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       if (e_html_editor_selection_is_subscript (selection) == subscript)
+               return;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT;
+       e_html_editor_view_exec_command (view, command, NULL);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "subscript");
+}
+
+/**
+ * e_html_editor_selection_is_superscript:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is in superscript.
+ *
+ * Returns @TRUE when selection is in superscript, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_superscript (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_val_if_fail (view != NULL, FALSE);
+
+       if (!e_html_editor_view_get_html_mode (view)) {
+               g_object_unref (view);
+               return FALSE;
+       }
+
+       g_object_unref (view);
+
+       range = html_editor_selection_get_current_range (selection);
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+       while (node) {
+               gchar *tag_name;
+
+               tag_name = webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (node));
+
+               if (g_ascii_strncasecmp (tag_name, "sup", 3) == 0) {
+                       g_free (tag_name);
+                       break;
+               }
+
+               g_free (tag_name);
+               node = webkit_dom_node_get_parent_node (node);
+       }
+
+       return (node != NULL);
+}
+
+/**
+ * e_html_editor_selection_set_superscript:
+ * @selection: an #EHTMLEditorSelection
+ * @superscript: @TRUE to enable superscript, @FALSE to disable
+ *
+ * Toggles superscript of current selection or letter at current cursor position,
+ * depending on whether @superscript is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_superscript (EHTMLEditorSelection *selection,
+                                         gboolean superscript)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       if (e_html_editor_selection_is_superscript (selection) == superscript)
+               return;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT;
+       e_html_editor_view_exec_command (view, command, NULL);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "superscript");
+}
+
+/**
+ * e_html_editor_selection_is_underline:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is underlined.
+ *
+ * Returns @TRUE when selection is underlined, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_selection_is_underline (EHTMLEditorSelection *selection)
+{
+       gboolean ret_val;
+       gchar *value, *text_content;
+       EHTMLEditorView *view;
+       WebKitDOMCSSStyleDeclaration *style;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMNode *node;
+       WebKitDOMElement *element;
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), FALSE);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_val_if_fail (view != NULL, FALSE);
+
+       if (!e_html_editor_view_get_html_mode (view)) {
+               g_object_unref (view);
+               return FALSE;
+       }
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       g_object_unref (view);
+       window = webkit_dom_document_get_default_view (document);
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return FALSE;
+
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+       /* If we are changing the format of block we have to re-set underline property,
+        * otherwise it will be turned off because of no text in composer */
+       text_content = webkit_dom_node_get_text_content (node);
+       if (g_strcmp0 (text_content, "") == 0) {
+               g_free (text_content);
+               return selection->priv->is_underline;
+       }
+       g_free (text_content);
+
+       if (WEBKIT_DOM_IS_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = webkit_dom_node_get_parent_element (node);
+
+       style = webkit_dom_dom_window_get_computed_style (window, element, NULL);
+       value = webkit_dom_css_style_declaration_get_property_value (style, "text-decoration");
+
+       if (g_strstr_len (value, -1, "underline"))
+               ret_val = TRUE;
+       else
+               ret_val = get_has_style (selection, "u");
+
+       g_free (value);
+       return ret_val;
+}
+
+/**
+ * e_html_editor_selection_set_underline:
+ * @selection: an #EHTMLEditorSelection
+ * @underline: @TRUE to enable underline, @FALSE to disable
+ *
+ * Toggles underline formatting of current selection or letter at current
+ * cursor position, depending on whether @underline is @TRUE or @FALSE.
+ */
+void
+e_html_editor_selection_set_underline (EHTMLEditorSelection *selection,
+                                       gboolean underline)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       if (e_html_editor_selection_is_underline (selection) == underline)
+               return;
+
+       selection->priv->is_underline = underline;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE;
+       e_html_editor_view_exec_command (view, command, NULL);
+
+       g_object_unref (view);
+
+       g_object_notify (G_OBJECT (selection), "underline");
+}
+
+/**
+ * e_html_editor_selection_unlink:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Removes any links (&lt;A&gt; elements) from current selection or at current
+ * cursor position.
+ */
+void
+e_html_editor_selection_unlink (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+       WebKitDOMElement *link;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       link = e_html_editor_dom_node_find_parent_element (
+                       webkit_dom_range_get_start_container (range, NULL), "A");
+
+       if (!link) {
+               gchar *text;
+               /* get element that was clicked on */
+               link = e_html_editor_view_get_element_under_mouse_click (view);
+               if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link))
+                       link = NULL;
+
+               text = webkit_dom_html_element_get_inner_text (
+                               WEBKIT_DOM_HTML_ELEMENT (link));
+               webkit_dom_html_element_set_outer_html (WEBKIT_DOM_HTML_ELEMENT (link), text, NULL);
+               g_free (text);
+       } else {
+               command = E_HTML_EDITOR_VIEW_COMMAND_UNLINK;
+               e_html_editor_view_exec_command (view, command, NULL);
+       }
+       g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_create_link:
+ * @selection: an #EHTMLEditorSelection
+ * @uri: destination of the new link
+ *
+ * Converts current selection into a link pointing to @url.
+ */
+void
+e_html_editor_selection_create_link (EHTMLEditorSelection *selection,
+                                     const gchar *uri)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+       g_return_if_fail (uri != NULL && *uri != '\0');
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK;
+       e_html_editor_view_exec_command (view, command, uri);
+
+       g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_insert_text:
+ * @selection: an #EHTMLEditorSelection
+ * @plain_text: text to insert
+ *
+ * Inserts @plain_text at current cursor position. When a text range is selected,
+ * it will be replaced by @plain_text.
+ */
+void
+e_html_editor_selection_insert_text (EHTMLEditorSelection *selection,
+                                     const gchar *plain_text)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+       g_return_if_fail (plain_text != NULL);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT;
+       e_html_editor_view_exec_command (view, command, plain_text);
+
+       g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_insert_html:
+ * @selection: an #EHTMLEditorSelection
+ * @html_text: an HTML code to insert
+ *
+ * Insert @html_text into document at current cursor position. When a text range
+ * is selected, it will be replaced by @html_text.
+ */
+void
+e_html_editor_selection_insert_html (EHTMLEditorSelection *selection,
+                                     const gchar *html_text)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewCommand command;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+       g_return_if_fail (html_text != NULL);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML;
+       if (e_html_editor_view_get_html_mode (view)) {
+               e_html_editor_view_exec_command (view, command, html_text);
+       } else {
+               e_html_editor_view_convert_and_insert_html_to_plain_text (
+                       view, html_text);
+       }
+
+       g_object_unref (view);
+}
+
+
+/************************* image_load_and_insert_async() *************************/
+
+typedef struct _LoadContext LoadContext;
+
+struct _LoadContext {
+       EHTMLEditorSelection *selection;
+       WebKitDOMElement *element;
+       GInputStream *input_stream;
+       GOutputStream *output_stream;
+       GFile *file;
+       GFileInfo *file_info;
+       goffset total_num_bytes;
+       gssize bytes_read;
+       const gchar *content_type;
+       const gchar *filename;
+       gchar buffer[4096];
+};
+
+/* Forward Declaration */
+static void
+image_load_stream_read_cb (GInputStream *input_stream,
+                           GAsyncResult *result,
+                           LoadContext *load_context);
+
+static LoadContext *
+image_load_context_new (EHTMLEditorSelection *selection)
+{
+       LoadContext *load_context;
+
+       load_context = g_slice_new0 (LoadContext);
+       load_context->selection = selection;
+
+       return load_context;
+}
+
+static void
+image_load_context_free (LoadContext *load_context)
+{
+       if (load_context->input_stream != NULL)
+               g_object_unref (load_context->input_stream);
+
+       if (load_context->output_stream != NULL)
+               g_object_unref (load_context->output_stream);
+
+       if (load_context->file_info != NULL)
+               g_object_unref (load_context->file_info);
+
+       if (load_context->file != NULL)
+               g_object_unref (load_context->file);
+
+       g_slice_free (LoadContext, load_context);
+}
+
+static void
+replace_base64_image_src (EHTMLEditorSelection *selection,
+                          WebKitDOMElement *element,
+                          const gchar *base64_content,
+                          const gchar *filename,
+                          const gchar *uri)
+{
+       EHTMLEditorView *view;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       e_html_editor_view_set_changed (view, TRUE);
+       g_object_unref (view);
+
+       webkit_dom_html_image_element_set_src (
+               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element),
+               base64_content);
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (element), "data-uri", uri, NULL);
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (element), "data-inline", "", NULL);
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (element), "data-name",
+               filename ? filename : "", NULL);
+}
+
+static void
+insert_base64_image (EHTMLEditorSelection *selection,
+                     const gchar *base64_content,
+                     const gchar *filename,
+                     const gchar *uri)
+{
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element, *caret_position, *resizable_wrapper;
+       WebKitDOMText *text;
+
+       caret_position = e_html_editor_selection_save_caret_position (selection);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       document = webkit_web_view_get_dom_document (
+               WEBKIT_WEB_VIEW (view));
+
+       e_html_editor_view_set_changed (view, TRUE);
+       g_object_unref (view);
+
+       resizable_wrapper =
+               webkit_dom_document_create_element (document, "span", NULL);
+       webkit_dom_element_set_attribute (
+               resizable_wrapper, "class", "-x-evo-resizable-wrapper", NULL);
+
+       element = webkit_dom_document_create_element (document, "img", NULL);
+       webkit_dom_html_image_element_set_src (
+               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element),
+               base64_content);
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (element), "data-uri", uri, NULL);
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (element), "data-inline", "", NULL);
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (element), "data-name",
+               filename ? filename : "", NULL);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (resizable_wrapper),
+               WEBKIT_DOM_NODE (element),
+               NULL);
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (caret_position)),
+               WEBKIT_DOM_NODE (resizable_wrapper),
+               WEBKIT_DOM_NODE (caret_position),
+               NULL);
+
+       /* We have to again use UNICODE_ZERO_WIDTH_SPACE character to restore
+        * caret on right position */
+       text = webkit_dom_document_create_text_node (
+               document, UNICODE_ZERO_WIDTH_SPACE);
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (caret_position)),
+               WEBKIT_DOM_NODE (text),
+               WEBKIT_DOM_NODE (caret_position),
+               NULL);
+
+       e_html_editor_selection_restore_caret_position (selection);
+}
+
+static void
+image_load_finish (LoadContext *load_context)
+{
+       EHTMLEditorSelection *selection;
+       GMemoryOutputStream *output_stream;
+       gchar *base64_encoded, *mime_type, *output, *uri;
+       gsize size;
+       gpointer data;
+
+       output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream);
+
+       selection = load_context->selection;
+
+       mime_type = g_content_type_get_mime_type (load_context->content_type);
+
+       data = g_memory_output_stream_get_data (output_stream);
+       size = g_memory_output_stream_get_data_size (output_stream);
+       uri = g_file_get_uri (load_context->file);
+
+       base64_encoded = g_base64_encode ((const guchar *) data, size);
+       output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL);
+       if (load_context->element)
+               replace_base64_image_src (
+                       selection, load_context->element, output, load_context->filename, uri);
+       else
+               insert_base64_image (selection, output, load_context->filename, uri);
+
+       g_free (base64_encoded);
+       g_free (output);
+       g_free (mime_type);
+       g_free (uri);
+
+       image_load_context_free (load_context);
+}
+
+static void
+image_load_write_cb (GOutputStream *output_stream,
+                     GAsyncResult *result,
+                     LoadContext *load_context)
+{
+       GInputStream *input_stream;
+       gssize bytes_written;
+       GError *error = NULL;
+
+       bytes_written = g_output_stream_write_finish (
+               output_stream, result, &error);
+
+       if (error) {
+               image_load_context_free (load_context);
+               return;
+       }
+
+       input_stream = load_context->input_stream;
+
+       if (bytes_written < load_context->bytes_read) {
+               g_memmove (
+                       load_context->buffer,
+                       load_context->buffer + bytes_written,
+                       load_context->bytes_read - bytes_written);
+               load_context->bytes_read -= bytes_written;
+
+               g_output_stream_write_async (
+                       output_stream,
+                       load_context->buffer,
+                       load_context->bytes_read,
+                       G_PRIORITY_DEFAULT, NULL,
+                       (GAsyncReadyCallback) image_load_write_cb,
+                       load_context);
+       } else
+               g_input_stream_read_async (
+                       input_stream,
+                       load_context->buffer,
+                       sizeof (load_context->buffer),
+                       G_PRIORITY_DEFAULT, NULL,
+                       (GAsyncReadyCallback) image_load_stream_read_cb,
+                       load_context);
+}
+
+static void
+image_load_stream_read_cb (GInputStream *input_stream,
+                           GAsyncResult *result,
+                           LoadContext *load_context)
+{
+       GOutputStream *output_stream;
+       gssize bytes_read;
+       GError *error = NULL;
+
+       bytes_read = g_input_stream_read_finish (
+               input_stream, result, &error);
+
+       if (error) {
+               image_load_context_free (load_context);
+               return;
+       }
+
+       if (bytes_read == 0) {
+               image_load_finish (load_context);
+               return;
+       }
+
+       output_stream = load_context->output_stream;
+       load_context->bytes_read = bytes_read;
+
+       g_output_stream_write_async (
+               output_stream,
+               load_context->buffer,
+               load_context->bytes_read,
+               G_PRIORITY_DEFAULT, NULL,
+               (GAsyncReadyCallback) image_load_write_cb,
+               load_context);
+}
+
+static void
+image_load_file_read_cb (GFile *file,
+                         GAsyncResult *result,
+                         LoadContext *load_context)
+{
+       GFileInputStream *input_stream;
+       GOutputStream *output_stream;
+       GError *error = NULL;
+
+       /* Input stream might be NULL, so don't use cast macro. */
+       input_stream = g_file_read_finish (file, result, &error);
+       load_context->input_stream = (GInputStream *) input_stream;
+
+       if (error) {
+               image_load_context_free (load_context);
+               return;
+       }
+
+       /* Load the contents into a GMemoryOutputStream. */
+       output_stream = g_memory_output_stream_new (
+               NULL, 0, g_realloc, g_free);
+
+       load_context->output_stream = output_stream;
+
+       g_input_stream_read_async (
+               load_context->input_stream,
+               load_context->buffer,
+               sizeof (load_context->buffer),
+               G_PRIORITY_DEFAULT, NULL,
+               (GAsyncReadyCallback) image_load_stream_read_cb,
+               load_context);
+}
+
+static void
+image_load_query_info_cb (GFile *file,
+                          GAsyncResult *result,
+                          LoadContext *load_context)
+{
+       GFileInfo *file_info;
+       GError *error = NULL;
+
+       file_info = g_file_query_info_finish (file, result, &error);
+       if (error) {
+               image_load_context_free (load_context);
+               return;
+       }
+
+       load_context->content_type = g_file_info_get_content_type (file_info);
+       load_context->total_num_bytes = g_file_info_get_size (file_info);
+       load_context->filename = g_file_info_get_name (file_info);
+
+       g_file_read_async (
+               file, G_PRIORITY_DEFAULT,
+               NULL, (GAsyncReadyCallback)
+               image_load_file_read_cb, load_context);
+}
+
+static void
+image_load_and_insert_async (EHTMLEditorSelection *selection,
+                             WebKitDOMElement *element,
+                             const gchar *uri)
+{
+       LoadContext *load_context;
+       GFile *file;
+
+       g_return_if_fail (uri && *uri);
+
+       file = g_file_new_for_uri (uri);
+       g_return_if_fail (file != NULL);
+
+       load_context = image_load_context_new (selection);
+       load_context->file = file;
+       load_context->element = element;
+
+       g_file_query_info_async (
+               file, "standard::*",
+               G_FILE_QUERY_INFO_NONE,G_PRIORITY_DEFAULT,
+               NULL, (GAsyncReadyCallback)
+               image_load_query_info_cb, load_context);
+}
+
+/**
+ * e_html_editor_selection_insert_image:
+ * @selection: an #EHTMLEditorSelection
+ * @image_uri: an URI of the source image
+ *
+ * Inserts image at current cursor position using @image_uri as source. When a
+ * text range is selected, it will be replaced by the image.
+ */
+void
+e_html_editor_selection_insert_image (EHTMLEditorSelection *selection,
+                                      const gchar *image_uri)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+       g_return_if_fail (image_uri != NULL);
+
+       if (is_in_html_mode (selection)) {
+               if (strstr (image_uri, ";base64,")) {
+                       if (g_str_has_prefix (image_uri, "data:"))
+                               insert_base64_image (selection, image_uri, "", "");
+                       if (strstr (image_uri, ";data")) {
+                               const gchar *base64_data = strstr (image_uri, ";") + 1;
+                               gchar *filename;
+                               glong filename_length;
+
+                               filename_length =
+                                       g_utf8_strlen (image_uri, -1) -
+                                       g_utf8_strlen (base64_data, -1) - 1;
+                               filename = g_strndup (image_uri, filename_length);
+
+                               insert_base64_image (selection, base64_data, filename, "");
+                               g_free (filename);
+                       }
+               } else
+                       image_load_and_insert_async (selection, NULL, image_uri);
+       }
+}
+
+/**
+ * e_html_editor_selection_replace_image_src:
+ * @selection: an #EHTMLEditorSelection
+ * @image: #WebKitDOMElement representation of image
+ * @image_uri: an URI of the source image
+ *
+ * Replace the src attribute of the given @image with @image_uri.
+ */
+void
+e_html_editor_selection_replace_image_src (EHTMLEditorSelection *selection,
+                                           WebKitDOMElement *image,
+                                           const gchar *image_uri)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+       g_return_if_fail (image_uri != NULL);
+       g_return_if_fail (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (image));
+
+       if (strstr (image_uri, ";base64,")) {
+               if (g_str_has_prefix (image_uri, "data:"))
+                       replace_base64_image_src (
+                               selection, image, image_uri, "", "");
+               if (strstr (image_uri, ";data")) {
+                       const gchar *base64_data = strstr (image_uri, ";") + 1;
+                       gchar *filename;
+                       glong filename_length;
+
+                       filename_length =
+                               g_utf8_strlen (image_uri, -1) -
+                               g_utf8_strlen (base64_data, -1) - 1;
+                       filename = g_strndup (image_uri, filename_length);
+
+                       replace_base64_image_src (
+                               selection, image, base64_data, filename, "");
+                       g_free (filename);
+               }
+       } else
+               image_load_and_insert_async (selection, image, image_uri);
+}
+
+/**
+ * e_html_editor_selection_clear_caret_position_marker:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Removes previously set caret position marker from composer.
+ */
+void
+e_html_editor_selection_clear_caret_position_marker (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       element = webkit_dom_document_get_element_by_id (document, "-x-evo-caret-position");
+
+       if (element)
+               remove_node (WEBKIT_DOM_NODE (element));
+
+       g_object_unref (view);
+}
+
+WebKitDOMNode *
+e_html_editor_selection_get_caret_position_node (WebKitDOMDocument *document)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_create_element (document, "SPAN", NULL);
+       webkit_dom_element_set_id (element, "-x-evo-caret-position");
+       webkit_dom_element_set_attribute (
+               element, "style", "color: red", NULL);
+       webkit_dom_html_element_set_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (element), "*", NULL);
+
+       return WEBKIT_DOM_NODE (element);
+}
+
+/**
+ * e_html_editor_selection_save_caret_position:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Saves current caret position in composer.
+ *
+ * Returns: #WebKitDOMElement that was created on caret position
+ */
+WebKitDOMElement *
+e_html_editor_selection_save_caret_position (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMNode *split_node;
+       WebKitDOMNode *start_offset_node;
+       WebKitDOMNode *caret_node;
+       WebKitDOMRange *range;
+       gulong start_offset;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_val_if_fail (view != NULL, NULL);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       g_object_unref (view);
+
+       e_html_editor_selection_clear_caret_position_marker (selection);
+
+       range = html_editor_selection_get_current_range (selection);
+       if (!range)
+               return NULL;
+
+       start_offset = webkit_dom_range_get_start_offset (range, NULL);
+       start_offset_node = webkit_dom_range_get_end_container (range, NULL);
+
+       caret_node = e_html_editor_selection_get_caret_position_node (document);
+
+       if (WEBKIT_DOM_IS_TEXT (start_offset_node) && start_offset != 0) {
+               WebKitDOMText *split_text;
+
+               split_text = webkit_dom_text_split_text (
+                               WEBKIT_DOM_TEXT (start_offset_node),
+                               start_offset, NULL);
+               split_node = WEBKIT_DOM_NODE (split_text);
+       } else {
+               split_node = start_offset_node;
+       }
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (start_offset_node),
+               caret_node,
+               split_node,
+               NULL);
+
+       return WEBKIT_DOM_ELEMENT (caret_node);
+}
+
+static void
+fix_quoting_nodes_after_caret_restoration (WebKitDOMDOMSelection *window_selection,
+                                           WebKitDOMNode *prev_sibling,
+                                           WebKitDOMNode *next_sibling)
+{
+       WebKitDOMNode *tmp_node;
+
+       if (!element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-temp-text-wrapper"))
+               return;
+
+       webkit_dom_dom_selection_modify (
+               window_selection, "move", "forward", "character");
+       tmp_node = webkit_dom_node_get_next_sibling (
+               webkit_dom_node_get_first_child (prev_sibling));
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (prev_sibling),
+               tmp_node,
+               next_sibling,
+               NULL);
+
+       tmp_node = webkit_dom_node_get_first_child (prev_sibling);
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (prev_sibling),
+               tmp_node,
+               webkit_dom_node_get_previous_sibling (next_sibling),
+               NULL);
+
+       remove_node (prev_sibling);
+
+       webkit_dom_dom_selection_modify (
+               window_selection, "move", "backward", "character");
+}
+
+/**
+ * e_html_editor_selection_restore_caret_position:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Restores previously saved caret position in composer.
+ */
+void
+e_html_editor_selection_restore_caret_position (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+       gboolean fix_after_quoting;
+       gboolean swap_direction = FALSE;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       g_object_unref (view);
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-caret-position");
+       fix_after_quoting = element_has_class (element, "-x-evo-caret-quoting");
+
+       if (element) {
+               WebKitDOMDOMWindow *window;
+               WebKitDOMNode *parent_node;
+               WebKitDOMDOMSelection *window_selection;
+               WebKitDOMNode *prev_sibling;
+               WebKitDOMNode *next_sibling;
+
+               if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)))
+                       swap_direction = TRUE;
+
+               window = webkit_dom_document_get_default_view (document);
+               window_selection = webkit_dom_dom_window_get_selection (window);
+               parent_node = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+               /* If parent is BODY element, we try to restore the position on the 
+                * element that is next to us */
+               if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent_node)) {
+                       /* Look if we have DIV on right */
+                       next_sibling = webkit_dom_node_get_next_sibling (
+                               WEBKIT_DOM_NODE (element));
+                       if (!WEBKIT_DOM_IS_ELEMENT (next_sibling)) {
+                               e_html_editor_selection_clear_caret_position_marker (selection);
+                               return;
+                       }
+
+                       if (element_has_class (WEBKIT_DOM_ELEMENT (next_sibling), "-x-evo-paragraph")) {
+                               remove_node (WEBKIT_DOM_NODE (element));
+
+                               move_caret_into_element (
+                                       document, WEBKIT_DOM_ELEMENT (next_sibling));
+
+                               goto out;
+                       }
+               }
+
+               move_caret_into_element (document, element);
+
+               if (fix_after_quoting) {
+                       prev_sibling = webkit_dom_node_get_previous_sibling (
+                               WEBKIT_DOM_NODE (element));
+                       next_sibling = webkit_dom_node_get_next_sibling (
+                               WEBKIT_DOM_NODE (element));
+                       if (!next_sibling)
+                               fix_after_quoting = FALSE;
+               }
+
+               remove_node (WEBKIT_DOM_NODE (element));
+
+               if (fix_after_quoting)
+                       fix_quoting_nodes_after_caret_restoration (
+                               window_selection, prev_sibling, next_sibling);
+ out:
+               /* FIXME If caret position is restored and afterwards the
+                * position is saved it is not on the place where it supposed
+                * to be (it is in the beginning of parent's element. It can
+                * be avoided by moving with the caret. */
+               if (swap_direction) {
+                       webkit_dom_dom_selection_modify (
+                               window_selection, "move", "forward", "character");
+                       webkit_dom_dom_selection_modify (
+                               window_selection, "move", "backward", "character");
+               } else {
+                       webkit_dom_dom_selection_modify (
+                               window_selection, "move", "backward", "character");
+                       webkit_dom_dom_selection_modify (
+                               window_selection, "move", "forward", "character");
+               }
+       }
+}
+
+static gint
+find_where_to_break_line (WebKitDOMNode *node,
+                          gint max_len,
+                         gint word_wrap_length)
+{
+       gchar *str, *text_start;
+       gunichar uc;
+       gint pos;
+       gint last_space = 0;
+       gint length;
+       gint ret_val = 0;
+       gchar* position;
+
+       text_start =  webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (node));
+       length = g_utf8_strlen (text_start, -1);
+
+       pos = 1;
+       last_space = 0;
+       str = text_start;
+       do {
+               uc = g_utf8_get_char (str);
+               if (!uc) {
+                       g_free (text_start);
+                       if (pos <= max_len)
+                               return pos;
+                       else
+                               return last_space;
+               }
+
+               /* If last_space is zero then the word is longer than
+                * word_wrap_length characters, so continue until we find
+                * a space */
+               if ((pos > max_len) && (last_space > 0)) {
+                       if (last_space > word_wrap_length) {
+                               g_free (text_start);
+                               return last_space;
+                       }
+
+                       if (last_space > max_len) {
+                               if (g_unichar_isspace (g_utf8_get_char (text_start)))
+                                       ret_val = 1;
+
+                               g_free (text_start);
+                               return ret_val;
+                       }
+
+                       if (last_space == max_len - 1) {
+                               uc = g_utf8_get_char (str);
+                               if (g_unichar_isspace (uc))
+                                       last_space++;
+                       }
+
+                       g_free (text_start);
+                       return last_space;
+               }
+
+               if (g_unichar_isspace (uc))
+                       last_space = pos;
+
+               pos += 1;
+               str = g_utf8_next_char (str);
+       } while (*str);
+
+       position = g_utf8_offset_to_pointer (text_start, max_len);
+
+       if (g_unichar_isspace (g_utf8_get_char (position))) {
+               ret_val = max_len + 1;
+       } else {
+               if (last_space < max_len) {
+                       ret_val = last_space;
+               } else {
+                       if (length > word_wrap_length)
+                               ret_val = last_space;
+                       else
+                               ret_val = 0;
+               }
+       }
+
+       g_free (text_start);
+
+       return ret_val;
+}
+
+static WebKitDOMElement *
+wrap_lines (EHTMLEditorSelection *selection,
+           WebKitDOMNode *paragraph,
+           WebKitDOMDocument *document,
+           gboolean remove_all_br,
+           gint word_wrap_length)
+{
+       WebKitDOMNode *node, *start_node;
+       WebKitDOMNode *paragraph_clone;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMElement *element;
+       WebKitDOMNodeList *wrap_br;
+       gint len, ii, br_count;
+       gulong length_left;
+       glong paragraph_char_count;
+       gchar *text_content;
+
+       if (selection) {
+               paragraph_char_count = g_utf8_strlen (
+                       e_html_editor_selection_get_string (selection), -1);
+
+               fragment = webkit_dom_range_clone_contents (
+                       html_editor_selection_get_current_range (selection), NULL);
+
+               /* Select all BR elements or just ours that are used for wrapping.
+                * We are not removing user BR elements when this function is activated
+                * from Format->Wrap Lines action */
+               wrap_br = webkit_dom_document_fragment_query_selector_all (
+                       fragment,
+                       remove_all_br ? "br" : "br.-x-evo-wrap-br",
+                       NULL);
+       } else {
+               WebKitDOMElement *caret_node;
+
+               if (!webkit_dom_node_has_child_nodes (paragraph))
+                       return WEBKIT_DOM_ELEMENT (paragraph);
+
+               paragraph_clone = webkit_dom_node_clone_node (paragraph, TRUE);
+               caret_node = webkit_dom_element_query_selector (
+                       WEBKIT_DOM_ELEMENT (paragraph_clone),
+                       "span#-x-evo-caret-position", NULL);
+               text_content = webkit_dom_node_get_text_content (paragraph_clone);
+               paragraph_char_count = g_utf8_strlen (text_content, -1);
+               if (caret_node)
+                       paragraph_char_count--;
+               g_free (text_content);
+
+               wrap_br = webkit_dom_element_query_selector_all (
+                       WEBKIT_DOM_ELEMENT (paragraph_clone),
+                       remove_all_br ? "br" : "br.-x-evo-wrap-br",
+                       NULL);
+       }
+
+       /* And remove them */
+       br_count = webkit_dom_node_list_get_length (wrap_br);
+       for (ii = 0; ii < br_count; ii++)
+               remove_node (webkit_dom_node_list_item (wrap_br, ii));
+
+       if (selection)
+               node = WEBKIT_DOM_NODE (fragment);
+       else {
+               webkit_dom_node_normalize (paragraph_clone);
+               node = webkit_dom_node_get_first_child (paragraph_clone);
+               if (node) {
+                       text_content = webkit_dom_node_get_text_content (node);
+                       if (g_strcmp0 ("\n", text_content) == 0)
+                               node = webkit_dom_node_get_next_sibling (node);
+                       g_free (text_content);
+               }
+       }
+
+       start_node = node;
+       len = 0;
+       while (node) {
+               gint offset = 0;
+
+               if (WEBKIT_DOM_IS_TEXT (node)) {
+                       const gchar *newline;
+                       WebKitDOMNode *next_sibling;
+
+                       /* If there is temporary hidden space we remove it */
+                       text_content = webkit_dom_node_get_text_content (node);
+                       if (strstr (text_content, UNICODE_ZERO_WIDTH_SPACE)) {
+                               if (g_str_has_prefix (text_content, UNICODE_ZERO_WIDTH_SPACE))
+                                       webkit_dom_character_data_delete_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (node),
+                                               0,
+                                               1,
+                                               NULL);
+                               if (g_str_has_suffix (text_content, UNICODE_ZERO_WIDTH_SPACE))
+                                       webkit_dom_character_data_delete_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (node),
+                                               g_utf8_strlen (text_content, -1) - 1,
+                                               1,
+                                               NULL);
+                               g_free (text_content);
+                               text_content = webkit_dom_node_get_text_content (node);
+                       }
+                       newline = g_strstr_len (text_content, -1, "\n");
+
+                       next_sibling = node;
+                       while (newline) {
+                               WebKitDOMElement *element;
+
+                               next_sibling = WEBKIT_DOM_NODE (webkit_dom_text_split_text (
+                                       WEBKIT_DOM_TEXT (next_sibling),
+                                       g_utf8_pointer_to_offset (text_content, newline),
+                                       NULL));
+
+                               if (!next_sibling)
+                                       break;
+
+                               element = webkit_dom_document_create_element (
+                                       document, "BR", NULL);
+                               element_add_class (element, "-x-evo-temp-wrap-text-br");
+
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (next_sibling),
+                                       WEBKIT_DOM_NODE (element),
+                                       next_sibling,
+                                       NULL);
+
+                               g_free (text_content);
+
+                               text_content = webkit_dom_node_get_text_content (next_sibling);
+                               if (g_str_has_prefix (text_content, "\n")) {
+                                       webkit_dom_character_data_delete_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (next_sibling), 0, 1, NULL);
+                                       g_free (text_content);
+                                       text_content =
+                                               webkit_dom_node_get_text_content (next_sibling);
+                               }
+                               newline = g_strstr_len (text_content, -1, "\n");
+                       }
+                       g_free (text_content);
+               } else {
+                       /* If element is ANCHOR we wrap it separately */
+                       if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) {
+                               glong anchor_length;
+
+                               text_content = webkit_dom_node_get_text_content (node);
+                               anchor_length = g_utf8_strlen (text_content, -1);
+                               if (len + anchor_length > word_wrap_length) {
+                                       element = webkit_dom_document_create_element (
+                                               document, "BR", NULL);
+                                       element_add_class (element, "-x-evo-wrap-br");
+                                       webkit_dom_node_insert_before (
+                                               webkit_dom_node_get_parent_node (node),
+                                               WEBKIT_DOM_NODE (element),
+                                               node,
+                                               NULL);
+                                       len = anchor_length;
+                               } else
+                                       len += anchor_length;
+
+                               g_free (text_content);
+                               node = webkit_dom_node_get_next_sibling (node);
+                               continue;
+                       }
+
+                       if (is_caret_position_node (node)) {
+                               node = webkit_dom_node_get_next_sibling (node);
+                               continue;
+                       }
+
+                       /* When we are not removing user-entered BR elements (lines wrapped by user),
+                        * we need to skip those elements */
+                       if (!remove_all_br && WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) {
+                               if (!element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")) {
+                                       len = 0;
+                                       node = webkit_dom_node_get_next_sibling (node);
+                                       continue;
+                               }
+                       }
+                       goto next_node;
+               }
+
+               /* If length of this node + what we already have is still less
+                * then word_wrap_length characters, then just join it and continue to next
+                * node */
+               length_left = webkit_dom_character_data_get_length (
+                       WEBKIT_DOM_CHARACTER_DATA (node));
+
+               if ((length_left + len) < word_wrap_length) {
+                       len += length_left;
+                       goto next_node;
+               }
+
+               /* wrap until we have something */
+               while ((length_left + len) > word_wrap_length) {
+                       /* Find where we can line-break the node so that it
+                        * effectively fills the rest of current row */
+                       offset = find_where_to_break_line (
+                               node, word_wrap_length - len, word_wrap_length);
+
+                       element = webkit_dom_document_create_element (document, "BR", NULL);
+                       element_add_class (element, "-x-evo-wrap-br");
+
+                       if (offset > 0 && offset <= word_wrap_length) {
+                               if (offset != length_left)
+                                       webkit_dom_text_split_text (
+                                               WEBKIT_DOM_TEXT (node), offset, NULL);
+
+                               if (webkit_dom_node_get_next_sibling (node)) {
+                                       gchar *nd_content;
+                                       WebKitDOMNode *nd = webkit_dom_node_get_next_sibling (node);
+
+                                       nd = webkit_dom_node_get_next_sibling (node);
+                                       nd_content = webkit_dom_node_get_text_content (nd);
+                                       if (nd_content && *nd_content) {
+                                               if (g_str_has_prefix (nd_content, " "))
+                                                       webkit_dom_character_data_replace_data (
+                                                               WEBKIT_DOM_CHARACTER_DATA (nd), 0, 1, "", 
NULL);
+                                               g_free (nd_content);
+                                               nd_content = webkit_dom_node_get_text_content (nd);
+                                               if (g_strcmp0 (nd_content, UNICODE_NBSP) == 0)
+                                                       remove_node (nd);
+                                               g_free (nd_content);
+                                       }
+
+                                       webkit_dom_node_insert_before (
+                                               webkit_dom_node_get_parent_node (node),
+                                               WEBKIT_DOM_NODE (element),
+                                               nd,
+                                               NULL);
+                               } else {
+                                       webkit_dom_node_append_child (
+                                               webkit_dom_node_get_parent_node (node),
+                                               WEBKIT_DOM_NODE (element),
+                                               NULL);
+                               }
+                       } else if (offset > word_wrap_length) {
+                               if (offset != length_left)
+                                       webkit_dom_text_split_text (
+                                               WEBKIT_DOM_TEXT (node), offset + 1, NULL);
+
+                               if (webkit_dom_node_get_next_sibling (node)) {
+                                       gchar *nd_content;
+                                       WebKitDOMNode *nd = webkit_dom_node_get_next_sibling (node);
+
+                                       nd = webkit_dom_node_get_next_sibling (node);
+                                       nd_content = webkit_dom_node_get_text_content (nd);
+                                       if (nd_content && *nd_content) {
+                                               if (g_str_has_prefix (nd_content, " "))
+                                                       webkit_dom_character_data_replace_data (
+                                                               WEBKIT_DOM_CHARACTER_DATA (nd), 0, 1, "", 
NULL);
+                                               g_free (nd_content);
+                                               nd_content = webkit_dom_node_get_text_content (nd);
+                                               if (g_strcmp0 (nd_content, UNICODE_NBSP) == 0)
+                                                       remove_node (nd);
+                                               g_free (nd_content);
+                                       }
+
+                                       webkit_dom_node_insert_before (
+                                               webkit_dom_node_get_parent_node (node),
+                                               WEBKIT_DOM_NODE (element),
+                                               nd,
+                                               NULL);
+                               } else {
+                                       webkit_dom_node_append_child (
+                                               webkit_dom_node_get_parent_node (node),
+                                               WEBKIT_DOM_NODE (element),
+                                               NULL);
+                               }
+                               len = 0;
+                               break;
+                       } else {
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (node),
+                                       WEBKIT_DOM_NODE (element),
+                                       node,
+                                       NULL);
+                       }
+                       length_left = webkit_dom_character_data_get_length (
+                               WEBKIT_DOM_CHARACTER_DATA (node));
+
+                       len = 0;
+               }
+               len += length_left - offset;
+ next_node:
+               if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (node))
+                       len = 0;
+
+               /* Move to next node */
+               if (webkit_dom_node_has_child_nodes (node)) {
+                       node = webkit_dom_node_get_first_child (node);
+               } else if (webkit_dom_node_get_next_sibling (node)) {
+                       node = webkit_dom_node_get_next_sibling (node);
+               } else {
+                       if (webkit_dom_node_is_equal_node (node, start_node))
+                               break;
+
+                       node = webkit_dom_node_get_parent_node (node);
+                       if (node)
+                               node = webkit_dom_node_get_next_sibling (node);
+               }
+       }
+
+       if (selection) {
+               gchar *html;
+
+               /* Create a wrapper DIV and put the processed content into it */
+               element = webkit_dom_document_create_element (document, "DIV", NULL);
+               element_add_class (element, "-x-evo-paragraph");
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (element),
+                       WEBKIT_DOM_NODE (start_node),
+                       NULL);
+
+               webkit_dom_node_normalize (WEBKIT_DOM_NODE (element));
+               /* Get HTML code of the processed content */
+               html = webkit_dom_html_element_get_inner_html (WEBKIT_DOM_HTML_ELEMENT (element));
+
+               /* Overwrite the current selection be the processed content */
+               e_html_editor_selection_insert_html (selection, html);
+
+               g_free (html);
+
+               return NULL;
+       } else {
+               webkit_dom_node_normalize (paragraph_clone);
+
+               /* Replace paragraph with wrapped one */
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (paragraph),
+                       paragraph_clone,
+                       paragraph,
+                       NULL);
+
+               return WEBKIT_DOM_ELEMENT (paragraph_clone);
+       }
+}
+
+void
+e_html_editor_selection_set_indented_style (EHTMLEditorSelection *selection,
+                                            WebKitDOMElement *element,
+                                            gint width)
+{
+       EHTMLEditorSelectionAlignment alignment;
+       gchar *style;
+       const gchar *align_value;
+       gint word_wrap_length = (width == -1) ? selection->priv->word_wrap_length : width;
+       gint start = 0, end = 0;
+
+       alignment = e_html_editor_selection_get_alignment (selection);
+       align_value = get_css_alignment_value (alignment);
+
+       if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT)
+               start = SPACES_PER_INDENTATION;
+
+       if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER) {
+               start = SPACES_PER_INDENTATION;
+       }
+
+       if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT) {
+               start = 0;
+               end = SPACES_PER_INDENTATION;
+       }
+
+       webkit_dom_element_set_class_name (element, "-x-evo-indented");
+       /* We don't want vertical space bellow and above blockquote inserted by
+        * WebKit's User Agent Stylesheet. We have to override it through style attribute. */
+       if (is_in_html_mode (selection))
+               style = g_strdup_printf (
+                       "-webkit-margin-start: %dch; -webkit-margin-end : %dch; %s",
+                       start, end, align_value);
+       else
+               style = g_strdup_printf (
+                       "-webkit-margin-start: %dch; -webkit-margin-end : %dch; "
+                       "word-wrap: normal; width: %dch; %s",
+                       start, end, word_wrap_length, align_value);
+
+       webkit_dom_element_set_attribute (element, "style", style, NULL);
+       g_free (style);
+}
+
+WebKitDOMElement *
+e_html_editor_selection_get_indented_element (EHTMLEditorSelection *selection,
+                                              WebKitDOMDocument *document,
+                                              gint width)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_create_element (document, "BLOCKQUOTE", NULL);
+       e_html_editor_selection_set_indented_style (selection, element, width);
+
+       return element;
+}
+
+void
+e_html_editor_selection_set_paragraph_style (EHTMLEditorSelection *selection,
+                                             WebKitDOMElement *element,
+                                             gint width,
+                                             gint offset,
+                                             const gchar *style_to_add)
+{
+       EHTMLEditorSelectionAlignment alignment;
+       const gchar *align_value = NULL;
+       char *style = NULL;
+       gint word_wrap_length = (width == -1) ? selection->priv->word_wrap_length : width;
+
+       alignment = e_html_editor_selection_get_alignment (selection);
+       align_value = get_css_alignment_value (alignment);
+
+       element_add_class (element, "-x-evo-paragraph");
+       if (!is_in_html_mode (selection)) {
+               style = g_strdup_printf (
+                       "width: %dch; word-wrap: normal; %s %s",
+                       (word_wrap_length + offset), align_value, style_to_add);
+       } else {
+               if (*align_value || *style_to_add)
+                       style = g_strdup_printf (
+                               "%s %s", align_value, style_to_add);
+       }
+       if (style) {
+               webkit_dom_element_set_attribute (element, "style", style, NULL);
+               g_free (style);
+       }
+}
+
+WebKitDOMElement *
+e_html_editor_selection_get_paragraph_element (EHTMLEditorSelection *selection,
+                                               WebKitDOMDocument *document,
+                                               gint width,
+                                               gint offset)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_create_element (document, "DIV", NULL);
+       e_html_editor_selection_set_paragraph_style (selection, element, width, offset, "");
+
+       return element;
+}
+
+WebKitDOMElement *
+e_html_editor_selection_put_node_into_paragraph (EHTMLEditorSelection *selection,
+                                                 WebKitDOMDocument *document,
+                                                 WebKitDOMNode *node,
+                                                 WebKitDOMNode *caret_position)
+{
+       WebKitDOMRange *range;
+       WebKitDOMElement *container;
+
+       range = webkit_dom_document_create_range (document);
+       container = e_html_editor_selection_get_paragraph_element (selection, document, -1, 0);
+       webkit_dom_range_select_node (range, node, NULL);
+       webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (container), NULL);
+       /* We have to move caret position inside this container */
+       webkit_dom_node_append_child (WEBKIT_DOM_NODE (container), caret_position, NULL);
+
+       return container;
+}
+
+/**
+ * e_html_editor_selection_wrap_lines:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Wraps all lines in current selection to be 71 characters long.
+ */
+void
+e_html_editor_selection_wrap_lines (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+       WebKitDOMRange *range;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *active_paragraph, *caret;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       g_object_unref (view);
+
+       caret = e_html_editor_selection_save_caret_position (selection);
+       if (g_strcmp0 (e_html_editor_selection_get_string (selection), "") == 0) {
+               WebKitDOMNode *end_container;
+               WebKitDOMNode *parent;
+               WebKitDOMNode *paragraph;
+               gchar *text_content;
+
+               /* We need to save caret position and restore it after
+                * wrapping the selection, but we need to save it before we
+                * start to modify selection */
+               range = html_editor_selection_get_current_range (selection);
+               if (!range)
+                       return;
+
+               end_container = webkit_dom_range_get_common_ancestor_container (range, NULL);
+
+               /* Wrap only text surrounded in DIV and P tags */
+               parent = webkit_dom_node_get_parent_node(end_container);
+               if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent) ||
+                   WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (parent)) {
+                       element_add_class (
+                               WEBKIT_DOM_ELEMENT (parent), "-x-evo-paragraph");
+                       paragraph = parent;
+               } else {
+                       WebKitDOMElement *parent_div =
+                               e_html_editor_dom_node_find_parent_element (parent, "DIV");
+
+                       if (element_has_class (parent_div, "-x-evo-paragraph")) {
+                               paragraph = WEBKIT_DOM_NODE (parent_div);
+                       } else {
+                               if (!caret)
+                                       return;
+
+                               /* We try to select previous sibling */
+                               paragraph = webkit_dom_node_get_previous_sibling (
+                                       WEBKIT_DOM_NODE (caret));
+                               if (paragraph) {
+                                       /* When there is just text without container
+                                        * we have to surround it with paragraph div */
+                                       if (WEBKIT_DOM_IS_TEXT (paragraph))
+                                               paragraph = WEBKIT_DOM_NODE (
+                                                       e_html_editor_selection_put_node_into_paragraph (
+                                                               selection, document, paragraph,
+                                                               WEBKIT_DOM_NODE (caret)));
+                               } else {
+                                       /* When some weird element is selected, return */
+                                       e_html_editor_selection_clear_caret_position_marker (selection);
+                                       return;
+                               }
+                       }
+               }
+
+               if (!paragraph)
+                       return;
+
+               webkit_dom_element_remove_attribute (
+                       WEBKIT_DOM_ELEMENT (paragraph), "style");
+               webkit_dom_element_set_id (
+                       WEBKIT_DOM_ELEMENT (paragraph), "-x-evo-active-paragraph");
+
+               text_content = webkit_dom_node_get_text_content (paragraph);
+               /* If there is hidden space character in the beginning we remove it */
+               if (strstr (text_content, UNICODE_ZERO_WIDTH_SPACE)) {
+                       if (g_str_has_prefix (text_content, UNICODE_ZERO_WIDTH_SPACE)) {
+                               WebKitDOMNode *node;
+
+                               node = webkit_dom_node_get_first_child (paragraph);
+
+                               webkit_dom_character_data_delete_data (
+                                       WEBKIT_DOM_CHARACTER_DATA (node),
+                                       0,
+                                       1,
+                                       NULL);
+                       }
+                       if (g_str_has_suffix (text_content, UNICODE_ZERO_WIDTH_SPACE)) {
+                               WebKitDOMNode *node;
+
+                               node = webkit_dom_node_get_last_child (paragraph);
+
+                               webkit_dom_character_data_delete_data (
+                                       WEBKIT_DOM_CHARACTER_DATA (node),
+                                       g_utf8_strlen (text_content, -1) -1,
+                                       1,
+                                       NULL);
+                       }
+               }
+               g_free (text_content);
+
+               wrap_lines (
+                       NULL, paragraph, document, FALSE,
+                       selection->priv->word_wrap_length);
+
+       } else {
+               e_html_editor_selection_save_caret_position (selection);
+               /* If we have selection -> wrap it */
+               wrap_lines (
+                       selection, NULL, document, FALSE,
+                       selection->priv->word_wrap_length);
+       }
+
+       active_paragraph = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-active-paragraph");
+       /* We have to move caret on position where it was before modifying the text */
+       e_html_editor_selection_restore_caret_position (selection);
+
+       /* Set paragraph as non-active */
+       if (active_paragraph)
+               webkit_dom_element_remove_attribute (
+                       WEBKIT_DOM_ELEMENT (active_paragraph), "id");
+}
+
+WebKitDOMElement *
+e_html_editor_selection_wrap_paragraph_length (EHTMLEditorSelection *selection,
+                                               WebKitDOMElement *paragraph,
+                                               gint length)
+{
+       WebKitDOMDocument *document;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+       g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL);
+       g_return_val_if_fail (length > 10, NULL);
+
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (paragraph));
+
+       return wrap_lines (
+               NULL, WEBKIT_DOM_NODE (paragraph), document, FALSE, length);
+}
+
+void
+e_html_editor_selection_wrap_paragraphs_in_document (EHTMLEditorSelection *selection,
+                                                     WebKitDOMDocument *document)
+{
+       WebKitDOMNodeList *list;
+       gint ii, length;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       list = webkit_dom_document_query_selector_all (
+               document, "div.-x-evo-paragraph:not(#-x-evo-input-start)", NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+
+       for (ii = 0; ii < length; ii++) {
+               gint quote, citation_level;
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               citation_level = get_citation_level (node);
+               quote = citation_level ? citation_level + 1 : 0;
+
+               if (node_is_list (node)) {
+                       WebKitDOMNode *item = webkit_dom_node_get_first_child (node);
+
+                       while (item && WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
+                               e_html_editor_selection_wrap_paragraph_length (
+                                       selection,
+                                       WEBKIT_DOM_ELEMENT (item),
+                                       selection->priv->word_wrap_length - quote);
+                               item = webkit_dom_node_get_next_sibling (item);
+                       }
+               } else {
+                       e_html_editor_selection_wrap_paragraph_length (
+                               selection,
+                               WEBKIT_DOM_ELEMENT (node),
+                               selection->priv->word_wrap_length - quote);
+               }
+       }
+}
+
+WebKitDOMElement *
+e_html_editor_selection_wrap_paragraph (EHTMLEditorSelection *selection,
+                                        WebKitDOMElement *paragraph)
+{
+       gint indentation_level, citation_level, quote;
+       gint final_width, word_wrap_length, offset = 0;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SELECTION (selection), NULL);
+       g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL);
+
+       word_wrap_length = selection->priv->word_wrap_length;
+       indentation_level = get_indentation_level (paragraph);
+       citation_level = get_citation_level (WEBKIT_DOM_NODE (paragraph));
+
+       if (node_is_list (WEBKIT_DOM_NODE (paragraph)) ||
+           WEBKIT_DOM_IS_HTMLLI_ELEMENT (paragraph)) {
+
+               gint list_level = get_list_level (WEBKIT_DOM_NODE (paragraph));
+               indentation_level = 0;
+
+               if (list_level > 0)
+                       offset = list_level * -SPACES_PER_LIST_LEVEL;
+               else
+                       offset = -SPACES_PER_LIST_LEVEL;
+       }
+
+       quote = citation_level ? citation_level + 1 : 0;
+       quote *= 2;
+
+       final_width = word_wrap_length - quote + offset;
+       final_width -= SPACES_PER_INDENTATION * indentation_level;
+
+       return e_html_editor_selection_wrap_paragraph_length (
+               selection, WEBKIT_DOM_ELEMENT (paragraph), final_width);
+}
+
+/**
+ * e_html_editor_selection_save:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Saves current cursor position or current selection range. The selection can
+ * be later restored by calling e_html_editor_selection_restore().
+ *
+ * Note that calling e_html_editor_selection_save() overwrites previously saved
+ * position.
+ *
+ * Note that this method inserts special markings into the HTML code that are
+ * used to later restore the selection. It can happen that by deleting some
+ * segments of the document some of the markings are deleted too. In that case
+ * restoring the selection by e_html_editor_selection_restore() can fail. Also by
+ * moving text segments (Cut & Paste) can result in moving the markings
+ * elsewhere, thus e_html_editor_selection_restore() will restore the selection
+ * incorrectly.
+ *
+ * It is recommended to use this method only when you are not planning to make
+ * bigger changes to content or structure of the document (formatting changes
+ * are usually OK).
+ */
+void
+e_html_editor_selection_save (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+       WebKitWebView *web_view;
+       WebKitDOMDocument *document;
+       WebKitDOMRange *range;
+       WebKitDOMNode *container;
+       WebKitDOMElement *marker;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       web_view = WEBKIT_WEB_VIEW (view);
+
+       document = webkit_web_view_get_dom_document (web_view);
+
+       /* First remove all markers (if present) */
+       marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       if (marker != NULL)
+               remove_node (WEBKIT_DOM_NODE (marker));
+
+       marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+       if (marker != NULL)
+               remove_node (WEBKIT_DOM_NODE (marker));
+
+       range = html_editor_selection_get_current_range (selection);
+
+       if (range != NULL) {
+               WebKitDOMNode *marker_node;
+               WebKitDOMNode *parent_node;
+               WebKitDOMNode *split_node;
+               glong offset;
+
+               marker = webkit_dom_document_create_element (
+                       document, "SPAN", NULL);
+               webkit_dom_element_set_id (
+                       marker, "-x-evo-selection-start-marker");
+
+               container = webkit_dom_range_get_start_container (range, NULL);
+               offset = webkit_dom_range_get_start_offset (range, NULL);
+
+               if (WEBKIT_DOM_IS_TEXT (container)) {
+                       WebKitDOMText *split_text;
+
+                       split_text = webkit_dom_text_split_text (
+                               WEBKIT_DOM_TEXT (container), offset, NULL);
+                       split_node = WEBKIT_DOM_NODE (split_text);
+               } else if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (container)) {
+                       webkit_dom_node_insert_before (
+                               container,
+                               WEBKIT_DOM_NODE (marker),
+                               webkit_dom_node_get_first_child (
+                                       WEBKIT_DOM_NODE (container)),
+                               NULL);
+
+                       goto end_marker;
+               } else {
+                       if (!webkit_dom_node_get_previous_sibling (container)) {
+                               split_node = webkit_dom_node_get_parent_node (
+                                       container);
+                       } else if (!webkit_dom_node_get_next_sibling (container)) {
+                               split_node = webkit_dom_node_get_parent_node (
+                                       container);
+                               split_node = webkit_dom_node_get_next_sibling (
+                                       split_node);
+                       } else
+                               split_node = container;
+               }
+
+               if (!split_node) {
+                       webkit_dom_node_insert_before (
+                               container,
+                               WEBKIT_DOM_NODE (marker),
+                               webkit_dom_node_get_first_child (
+                                       WEBKIT_DOM_NODE (container)),
+                               NULL);
+               } else {
+                       marker_node = WEBKIT_DOM_NODE (marker);
+                       parent_node = webkit_dom_node_get_parent_node (split_node);
+
+                       webkit_dom_node_insert_before (
+                               parent_node, marker_node, split_node, NULL);
+               }
+ end_marker:
+               marker = webkit_dom_document_create_element (
+                       document, "SPAN", NULL);
+               webkit_dom_element_set_id (
+                       marker, "-x-evo-selection-end-marker");
+
+               container = webkit_dom_range_get_end_container (range, NULL);
+               offset = webkit_dom_range_get_end_offset (range, NULL);
+
+               if (WEBKIT_DOM_IS_TEXT (container) && offset != 0) {
+                       WebKitDOMText *split_text;
+
+                       split_text = webkit_dom_text_split_text (
+                               WEBKIT_DOM_TEXT (container), offset, NULL);
+                       split_node = WEBKIT_DOM_NODE (split_text);
+               } else if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (container)) {
+                       webkit_dom_node_append_child (
+                               container, WEBKIT_DOM_NODE (marker), NULL);
+                       g_object_unref (view);
+                       return;
+               } else {
+                       if (!webkit_dom_node_get_previous_sibling (container)) {
+                               split_node = webkit_dom_node_get_parent_node (
+                                       container);
+                       } else if (!webkit_dom_node_get_next_sibling (container)) {
+                               split_node = webkit_dom_node_get_parent_node (
+                                       container);
+                               split_node = webkit_dom_node_get_next_sibling (
+                                       split_node);
+                       } else
+                               split_node = container;
+               }
+
+               marker_node = WEBKIT_DOM_NODE (marker);
+
+               if (split_node) {
+                       parent_node = webkit_dom_node_get_parent_node (split_node);
+
+                       webkit_dom_node_insert_before (
+                               parent_node, marker_node, split_node, NULL);
+               } else {
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (container),
+                               marker_node,
+                               NULL);
+               }
+       }
+
+       g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_restore:
+ * @selection: an #EHTMLEditorSelection
+ *
+ * Restores cursor position or selection range that was saved by
+ * e_html_editor_selection_save().
+ *
+ * Note that calling this function without calling e_html_editor_selection_save()
+ * before is a programming error and the behavior is undefined.
+ */
+void
+e_html_editor_selection_restore (EHTMLEditorSelection *selection)
+{
+       EHTMLEditorView *view;
+       WebKitWebView *web_view;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+       WebKitDOMElement *marker;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       web_view = WEBKIT_WEB_VIEW (view);
+
+       document = webkit_web_view_get_dom_document (web_view);
+       window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+       range = webkit_dom_document_create_range (document);
+
+       if (range != NULL) {
+               marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               if (!marker) {
+                       marker = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-end-marker");
+                       if (marker)
+                               remove_node (WEBKIT_DOM_NODE (marker));
+                       return;
+               }
+
+               webkit_dom_range_set_start_after (
+                       range, WEBKIT_DOM_NODE (marker), NULL);
+               remove_node (WEBKIT_DOM_NODE (marker));
+
+               marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+               if (!marker) {
+                       marker = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+                       if (marker)
+                               remove_node (WEBKIT_DOM_NODE (marker));
+                       return;
+               }
+
+               webkit_dom_range_set_end_before (
+                       range, WEBKIT_DOM_NODE (marker), NULL);
+               remove_node (WEBKIT_DOM_NODE (marker));
+
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+       }
+
+       g_object_unref (view);
+}
+
+static void
+html_editor_selection_modify (EHTMLEditorSelection *selection,
+                              const gchar *alter,
+                              gboolean forward,
+                              EHTMLEditorSelectionGranularity granularity)
+{
+       EHTMLEditorView *view;
+       WebKitWebView *web_view;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *dom_selection;
+       const gchar *granularity_str;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       web_view = WEBKIT_WEB_VIEW (view);
+
+       document = webkit_web_view_get_dom_document (web_view);
+       window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+
+       switch (granularity) {
+               case E_HTML_EDITOR_SELECTION_GRANULARITY_CHARACTER:
+                       granularity_str = "character";
+                       break;
+               case E_HTML_EDITOR_SELECTION_GRANULARITY_WORD:
+                       granularity_str = "word";
+                       break;
+       }
+
+       webkit_dom_dom_selection_modify (
+               dom_selection, alter,
+               forward ? "forward" : "backward",
+               granularity_str);
+
+       g_object_unref (view);
+}
+
+/**
+ * e_html_editor_selection_extend:
+ * @selection: an #EHTMLEditorSelection
+ * @forward: whether to extend selection forward or backward
+ * @granularity: granularity of the extension
+ *
+ * Extends current selection in given direction by given granularity.
+ */
+void
+e_html_editor_selection_extend (EHTMLEditorSelection *selection,
+                                gboolean forward,
+                                EHTMLEditorSelectionGranularity granularity)
+{
+       html_editor_selection_modify (selection, "extend", forward, granularity);
+}
+
+/**
+ * e_html_editor_selection_move:
+ * @selection: an #EHTMLEditorSelection
+ * @forward: whether to move the selection forward or backward
+ * @granularity: granularity of the movement
+ *
+ * Moves current selection in given direction by given granularity
+ */
+void
+e_html_editor_selection_move (EHTMLEditorSelection *selection,
+                              gboolean forward,
+                              EHTMLEditorSelectionGranularity granularity)
+{
+       html_editor_selection_modify (selection, "move", forward, granularity);
+}
+
+void
+e_html_editor_selection_scroll_to_caret (EHTMLEditorSelection *selection)
+{
+       WebKitDOMElement *caret;
+
+       caret = e_html_editor_selection_save_caret_position (selection);
+
+       webkit_dom_element_scroll_into_view (caret, TRUE);
+
+       e_html_editor_selection_clear_caret_position_marker (selection);
+}
diff --git a/e-util/e-html-editor-selection.h b/e-util/e-html-editor-selection.h
new file mode 100644
index 0000000..c0d2d18
--- /dev/null
+++ b/e-util/e-html-editor-selection.h
@@ -0,0 +1,250 @@
+/*
+ * e-html-editor-selection.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_SELECTION_H
+#define E_HTML_EDITOR_SELECTION_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-util-enums.h>
+#include <webkit/webkit.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_SELECTION \
+       (e_html_editor_selection_get_type ())
+#define E_HTML_EDITOR_SELECTION(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelection))
+#define E_HTML_EDITOR_SELECTION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelectionClass))
+#define E_IS_HTML_EDITOR_SELECTION(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_SELECTION))
+#define E_IS_HTML_EDITOR_SELECTION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_SELECTION))
+#define E_HTML_EDITOR_SELECTION_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_SELECTION, EHTMLEditorSelectionClass))
+
+G_BEGIN_DECLS
+
+struct _EHTMLEditorView;
+
+typedef struct _EHTMLEditorSelection EHTMLEditorSelection;
+typedef struct _EHTMLEditorSelectionClass EHTMLEditorSelectionClass;
+typedef struct _EHTMLEditorSelectionPrivate EHTMLEditorSelectionPrivate;
+
+struct _EHTMLEditorSelection {
+       GObject parent;
+       EHTMLEditorSelectionPrivate *priv;
+};
+
+struct _EHTMLEditorSelectionClass {
+       GObjectClass parent_class;
+};
+
+GType          e_html_editor_selection_get_type
+                                               (void) G_GNUC_CONST;
+struct _EHTMLEditorView *
+               e_html_editor_selection_ref_html_editor_view
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_block_selection_changed
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_unblock_selection_changed
+                                               (EHTMLEditorSelection *selection);
+gint           e_html_editor_selection_get_word_wrap_length
+                                               (EHTMLEditorSelection *selection);
+gboolean       e_html_editor_selection_has_text
+                                               (EHTMLEditorSelection *selection);
+gchar *                e_html_editor_selection_get_caret_word
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_replace_caret_word
+                                               (EHTMLEditorSelection *selection,
+                                                const gchar *replacement);
+EHTMLEditorSelectionAlignment
+               e_html_editor_selection_get_alignment
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_alignment
+                                               (EHTMLEditorSelection *selection,
+                                                EHTMLEditorSelectionAlignment alignment);
+const gchar *  e_html_editor_selection_get_background_color
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_background_color
+                                               (EHTMLEditorSelection *selection,
+                                                const gchar *color);
+void           e_html_editor_selection_get_font_color
+                                               (EHTMLEditorSelection *selection,
+                                                GdkRGBA *rgba);
+void           e_html_editor_selection_set_font_color
+                                               (EHTMLEditorSelection *selection,
+                                                const GdkRGBA *rgba);
+const gchar *  e_html_editor_selection_get_font_name
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_font_name
+                                               (EHTMLEditorSelection *selection,
+                                                const gchar *font_name);
+guint          e_html_editor_selection_get_font_size
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_font_size
+                                               (EHTMLEditorSelection *selection,
+                                                guint font_size);
+EHTMLEditorSelectionBlockFormat
+               e_html_editor_selection_get_block_format
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_block_format
+                                               (EHTMLEditorSelection *selection,
+                                                EHTMLEditorSelectionBlockFormat format);
+gboolean       e_html_editor_selection_is_citation
+                                               (EHTMLEditorSelection *selection);
+gboolean       e_html_editor_selection_is_indented
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_indent  (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_unindent
+                                               (EHTMLEditorSelection *selection);
+gboolean       e_html_editor_selection_is_bold (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_bold
+                                               (EHTMLEditorSelection *selection,
+                                                gboolean bold);
+gboolean       e_html_editor_selection_is_italic
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_italic
+                                               (EHTMLEditorSelection *selection,
+                                                gboolean italic);
+gboolean       e_html_editor_selection_is_monospaced
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_monospaced
+                                               (EHTMLEditorSelection *selection,
+                                                gboolean monospaced);
+gboolean       e_html_editor_selection_is_strikethrough
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_strikethrough
+                                               (EHTMLEditorSelection *selection,
+                                                gboolean strikethrough);
+gboolean       e_html_editor_selection_is_superscript
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_superscript
+                                               (EHTMLEditorSelection *selection,
+                                                gboolean superscript);
+gboolean       e_html_editor_selection_is_subscript
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_subscript
+                                               (EHTMLEditorSelection *selection,
+                                                gboolean subscript);
+gboolean       e_html_editor_selection_is_underline
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_underline
+                                               (EHTMLEditorSelection *selection,
+                                                gboolean underline);
+void           e_html_editor_selection_unlink  (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_create_link
+                                               (EHTMLEditorSelection *selection,
+                                                const gchar *uri);
+const gchar *  e_html_editor_selection_get_string
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_replace (EHTMLEditorSelection *selection,
+                                                const gchar *new_string);
+void           e_html_editor_selection_insert_html
+                                               (EHTMLEditorSelection *selection,
+                                                const gchar *html_text);
+void           e_html_editor_selection_replace_image_src
+                                               (EHTMLEditorSelection *selection,
+                                                WebKitDOMElement *image,
+                                                const gchar *image_uri);
+void           e_html_editor_selection_insert_image
+                                               (EHTMLEditorSelection *selection,
+                                                const gchar *image_uri);
+void           e_html_editor_selection_insert_text
+                                               (EHTMLEditorSelection *selection,
+                                                const gchar *plain_text);
+void           e_html_editor_selection_clear_caret_position_marker
+                                               (EHTMLEditorSelection *selection);
+WebKitDOMNode *
+               e_html_editor_selection_get_caret_position_node
+                                               (WebKitDOMDocument *document);
+WebKitDOMElement *
+               e_html_editor_selection_save_caret_position
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_restore_caret_position
+                                               (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_set_indented_style
+                                               (EHTMLEditorSelection *selection,
+                                                WebKitDOMElement *element,
+                                                gint width);
+WebKitDOMElement *
+               e_html_editor_selection_get_indented_element
+                                               (EHTMLEditorSelection *selection,
+                                                WebKitDOMDocument *document,
+                                                gint width);
+void           e_html_editor_selection_set_paragraph_style
+                                               (EHTMLEditorSelection *selection,
+                                                WebKitDOMElement *element,
+                                                gint width,
+                                                gint offset,
+                                                const gchar *style_to_add);
+WebKitDOMElement *
+               e_html_editor_selection_get_paragraph_element
+                                               (EHTMLEditorSelection *selection,
+                                                WebKitDOMDocument *document,
+                                                gint width,
+                                                gint offset);
+WebKitDOMElement *
+               e_html_editor_selection_put_node_into_paragraph
+                                               (EHTMLEditorSelection *selection,
+                                                WebKitDOMDocument *document,
+                                                WebKitDOMNode *node,
+                                                WebKitDOMNode *caret_position);
+void           e_html_editor_selection_wrap_lines
+                                               (EHTMLEditorSelection *selection);
+WebKitDOMElement *
+               e_html_editor_selection_wrap_paragraph_length
+                                               (EHTMLEditorSelection *selection,
+                                                WebKitDOMElement *paragraph,
+                                                gint length);
+void           e_html_editor_selection_wrap_paragraphs_in_document
+                                               (EHTMLEditorSelection *selection,
+                                                WebKitDOMDocument *document);
+WebKitDOMElement *
+               e_html_editor_selection_wrap_paragraph
+                                               (EHTMLEditorSelection *selection,
+                                                WebKitDOMElement *paragraph);
+void           e_html_editor_selection_save    (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_restore (EHTMLEditorSelection *selection);
+void           e_html_editor_selection_move    (EHTMLEditorSelection *selection,
+                                                gboolean forward,
+                                                EHTMLEditorSelectionGranularity granularity);
+void           e_html_editor_selection_extend  (EHTMLEditorSelection *selection,
+                                                gboolean forward,
+                                                EHTMLEditorSelectionGranularity granularity);
+void           e_html_editor_selection_scroll_to_caret
+                                               (EHTMLEditorSelection *selection);
+EHTMLEditorSelectionBlockFormat
+               e_html_editor_selection_get_list_format_from_node
+                                               (WebKitDOMNode *node);
+EHTMLEditorSelectionAlignment
+               e_html_editor_selection_get_list_alignment_from_node
+                                               (WebKitDOMNode *node);
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_SELECTION_H */
diff --git a/e-util/e-html-editor-spell-check-dialog.c b/e-util/e-html-editor-spell-check-dialog.c
new file mode 100644
index 0000000..b399dfa
--- /dev/null
+++ b/e-util/e-html-editor-spell-check-dialog.c
@@ -0,0 +1,710 @@
+/*
+ * e-html-editor-spell-dialog.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-spell-check-dialog.h"
+
+#include <glib/gi18n-lib.h>
+#include <enchant/enchant.h>
+
+#include "e-html-editor-view.h"
+#include "e-spell-checker.h"
+#include "e-spell-dictionary.h"
+
+#define E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG, EHTMLEditorSpellCheckDialogPrivate))
+
+struct _EHTMLEditorSpellCheckDialogPrivate {
+       GtkWidget *add_word_button;
+       GtkWidget *back_button;
+       GtkWidget *dictionary_combo;
+       GtkWidget *ignore_button;
+       GtkWidget *replace_button;
+       GtkWidget *replace_all_button;
+       GtkWidget *skip_button;
+       GtkWidget *suggestion_label;
+       GtkWidget *tree_view;
+
+       WebKitDOMDOMSelection *selection;
+
+       gchar *word;
+       ESpellDictionary *current_dict;
+};
+
+enum {
+       COLUMN_NAME,
+       COLUMN_DICTIONARY,
+       NUM_COLUMNS
+};
+
+G_DEFINE_TYPE (
+       EHTMLEditorSpellCheckDialog,
+       e_html_editor_spell_check_dialog,
+       E_TYPE_HTML_EDITOR_DIALOG)
+
+static void
+html_editor_spell_check_dialog_set_word (EHTMLEditorSpellCheckDialog *dialog,
+                                         const gchar *word)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       GtkTreeView *tree_view;
+       GtkListStore *store;
+       gchar *markup;
+       GList *list, *link;
+
+       if (word == NULL)
+               return;
+
+       if (dialog->priv->word != word) {
+               g_free (dialog->priv->word);
+               dialog->priv->word = g_strdup (word);
+       }
+
+       markup = g_strdup_printf (_("<b>Suggestions for '%s'</b>"), word);
+       gtk_label_set_markup (
+               GTK_LABEL (dialog->priv->suggestion_label), markup);
+       g_free (markup);
+
+       tree_view = GTK_TREE_VIEW (dialog->priv->tree_view);
+       store = GTK_LIST_STORE (gtk_tree_view_get_model (tree_view));
+       gtk_list_store_clear (store);
+
+       list = e_spell_dictionary_get_suggestions (
+               dialog->priv->current_dict, word, -1);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               GtkTreeIter iter;
+               gchar *suggestion = link->data;
+
+               gtk_list_store_append (store, &iter);
+               gtk_list_store_set (store, &iter, 0, suggestion, -1);
+       }
+
+       g_list_free_full (list, (GDestroyNotify) g_free);
+
+       /* We give focus to WebKit so that the currently selected word
+        * is highlited. Without focus selection is not visible (at
+        * least with my default color scheme). The focus in fact is not
+        * given to WebKit, because this dialog is modal, but it satisfies
+        * it in a way that it paints the selection :) */
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       gtk_widget_grab_focus (GTK_WIDGET (view));
+}
+
+static gboolean
+select_next_word (EHTMLEditorSpellCheckDialog *dialog)
+{
+       WebKitDOMNode *anchor, *focus;
+       gulong anchor_offset, focus_offset;
+
+       anchor = webkit_dom_dom_selection_get_anchor_node (dialog->priv->selection);
+       anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dialog->priv->selection);
+
+       focus = webkit_dom_dom_selection_get_focus_node (dialog->priv->selection);
+       focus_offset = webkit_dom_dom_selection_get_focus_offset (dialog->priv->selection);
+
+       /* Jump _behind_ next word */
+       webkit_dom_dom_selection_modify (
+               dialog->priv->selection, "move", "forward", "word");
+       /* Jump before the word */
+       webkit_dom_dom_selection_modify (
+               dialog->priv->selection, "move", "backward", "word");
+       /* Select it */
+       webkit_dom_dom_selection_modify (
+               dialog->priv->selection, "extend", "forward", "word");
+
+       /* If the selection didn't change, then we have most probably
+        * reached the end of document - return FALSE */
+       return !((anchor == webkit_dom_dom_selection_get_anchor_node (
+                               dialog->priv->selection)) &&
+                (anchor_offset == webkit_dom_dom_selection_get_anchor_offset (
+                               dialog->priv->selection)) &&
+                (focus == webkit_dom_dom_selection_get_focus_node (
+                               dialog->priv->selection)) &&
+                (focus_offset == webkit_dom_dom_selection_get_focus_offset (
+                               dialog->priv->selection)));
+}
+
+static gboolean
+html_editor_spell_check_dialog_next (EHTMLEditorSpellCheckDialog *dialog)
+{
+       WebKitDOMNode *start = NULL, *end = NULL;
+       gulong start_offset, end_offset;
+
+       if (dialog->priv->word == NULL) {
+               webkit_dom_dom_selection_modify (
+                       dialog->priv->selection, "move", "left", "documentboundary");
+       } else {
+               /* Remember last selected word */
+               start = webkit_dom_dom_selection_get_anchor_node (
+                       dialog->priv->selection);
+               end = webkit_dom_dom_selection_get_focus_node (
+                       dialog->priv->selection);
+               start_offset = webkit_dom_dom_selection_get_anchor_offset (
+                       dialog->priv->selection);
+               end_offset = webkit_dom_dom_selection_get_focus_offset (
+                       dialog->priv->selection);
+       }
+
+       while (select_next_word (dialog)) {
+               WebKitDOMRange *range;
+               WebKitSpellChecker *checker;
+               gint loc, len;
+               gchar *word;
+
+               range = webkit_dom_dom_selection_get_range_at (
+                       dialog->priv->selection, 0, NULL);
+               word = webkit_dom_range_get_text (range);
+
+               checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+               webkit_spell_checker_check_spelling_of_string (
+                       checker, word, &loc, &len);
+
+               /* Found misspelled word! */
+               if (loc != -1) {
+                       html_editor_spell_check_dialog_set_word (dialog, word);
+                       g_free (word);
+                       return TRUE;
+               }
+
+               g_free (word);
+       }
+
+       /* Restore the selection to contain the last misspelled word. This is
+        * reached only when we reach the end of the document */
+       if (start && end) {
+               webkit_dom_dom_selection_set_base_and_extent (
+                       dialog->priv->selection, start, start_offset,
+                       end, end_offset, NULL);
+       }
+
+       /* Close the dialog */
+       gtk_widget_hide (GTK_WIDGET (dialog));
+       return FALSE;
+}
+
+static gboolean
+select_previous_word (EHTMLEditorSpellCheckDialog *dialog)
+{
+       WebKitDOMNode *old_anchor_node;
+       WebKitDOMNode *new_anchor_node;
+       gulong old_anchor_offset;
+       gulong new_anchor_offset;
+
+       old_anchor_node = webkit_dom_dom_selection_get_anchor_node (
+               dialog->priv->selection);
+       old_anchor_offset = webkit_dom_dom_selection_get_anchor_offset (
+               dialog->priv->selection);
+
+       /* Jump on the beginning of current word */
+       webkit_dom_dom_selection_modify (
+               dialog->priv->selection, "move", "backward", "word");
+       /* Jump before previous word */
+       webkit_dom_dom_selection_modify (
+               dialog->priv->selection, "move", "backward", "word");
+       /* Select it */
+       webkit_dom_dom_selection_modify (
+               dialog->priv->selection, "extend", "forward", "word");
+
+       /* If the selection start didn't change, then we have most probably
+        * reached the beginnig of document. Return FALSE */
+
+       new_anchor_node = webkit_dom_dom_selection_get_anchor_node (
+               dialog->priv->selection);
+       new_anchor_offset = webkit_dom_dom_selection_get_anchor_offset (
+               dialog->priv->selection);
+
+       return (new_anchor_node != old_anchor_node) ||
+               (new_anchor_offset != old_anchor_offset);
+}
+
+static gboolean
+html_editor_spell_check_dialog_prev (EHTMLEditorSpellCheckDialog *dialog)
+{
+       WebKitDOMNode *start = NULL, *end = NULL;
+       gulong start_offset, end_offset;
+
+       if (dialog->priv->word == NULL) {
+               webkit_dom_dom_selection_modify (
+                       dialog->priv->selection,
+                       "move", "right", "documentboundary");
+               webkit_dom_dom_selection_modify (
+                       dialog->priv->selection,
+                       "extend", "backward", "word");
+       } else {
+               /* Remember last selected word */
+               start = webkit_dom_dom_selection_get_anchor_node (
+                       dialog->priv->selection);
+               end = webkit_dom_dom_selection_get_focus_node (
+                       dialog->priv->selection);
+               start_offset = webkit_dom_dom_selection_get_anchor_offset (
+                       dialog->priv->selection);
+               end_offset = webkit_dom_dom_selection_get_focus_offset (
+                       dialog->priv->selection);
+       }
+
+       while (select_previous_word (dialog)) {
+               WebKitDOMRange *range;
+               WebKitSpellChecker *checker;
+               gint loc, len;
+               gchar *word;
+
+               range = webkit_dom_dom_selection_get_range_at (
+                       dialog->priv->selection, 0, NULL);
+               word = webkit_dom_range_get_text (range);
+
+               checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+               webkit_spell_checker_check_spelling_of_string (
+                       checker, word, &loc, &len);
+
+               /* Found misspelled word! */
+               if (loc != -1) {
+                       html_editor_spell_check_dialog_set_word (dialog, word);
+                       g_free (word);
+                       return TRUE;
+               }
+
+               g_free (word);
+       }
+
+       /* Restore the selection to contain the last misspelled word. This is
+        * reached only when we reach the beginning of the document */
+       if (start && end) {
+               webkit_dom_dom_selection_set_base_and_extent (
+                       dialog->priv->selection, start, start_offset,
+                       end, end_offset, NULL);
+       }
+
+       /* Close the dialog */
+       gtk_widget_hide (GTK_WIDGET (dialog));
+       return FALSE;
+}
+
+static void
+html_editor_spell_check_dialog_replace (EHTMLEditorSpellCheckDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *editor_selection;
+       GtkTreeModel *model;
+       GtkTreeSelection *selection;
+       GtkTreeIter iter;
+       gchar *replacement;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       editor_selection = e_html_editor_view_get_selection (view);
+
+       selection = gtk_tree_view_get_selection (
+               GTK_TREE_VIEW (dialog->priv->tree_view));
+       gtk_tree_selection_get_selected (selection, &model, &iter);
+       gtk_tree_model_get (model, &iter, 0, &replacement, -1);
+
+       e_html_editor_selection_insert_html (
+               editor_selection, replacement);
+
+       g_free (replacement);
+       html_editor_spell_check_dialog_next (dialog);
+}
+
+static void
+html_editor_spell_check_dialog_replace_all (EHTMLEditorSpellCheckDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *editor_selection;
+       GtkTreeModel *model;
+       GtkTreeSelection *selection;
+       GtkTreeIter iter;
+       gchar *replacement;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       editor_selection = e_html_editor_view_get_selection (view);
+
+       selection = gtk_tree_view_get_selection (
+               GTK_TREE_VIEW (dialog->priv->tree_view));
+       gtk_tree_selection_get_selected (selection, &model, &iter);
+       gtk_tree_model_get (model, &iter, 0, &replacement, -1);
+
+       /* Repeatedly search for 'word', then replace selection by
+        * 'replacement'. Repeat until there's at least one occurence of
+        * 'word' in the document */
+       while (webkit_web_view_search_text (
+                       WEBKIT_WEB_VIEW (view), dialog->priv->word,
+                       FALSE, TRUE, TRUE)) {
+
+               e_html_editor_selection_insert_html (
+                       editor_selection, replacement);
+       }
+
+       g_free (replacement);
+       html_editor_spell_check_dialog_next (dialog);
+}
+
+static void
+html_editor_spell_check_dialog_ignore (EHTMLEditorSpellCheckDialog *dialog)
+{
+       if (dialog->priv->word == NULL)
+               return;
+
+       e_spell_dictionary_ignore_word (
+               dialog->priv->current_dict, dialog->priv->word, -1);
+
+       html_editor_spell_check_dialog_next (dialog);
+}
+
+static void
+html_editor_spell_check_dialog_learn (EHTMLEditorSpellCheckDialog *dialog)
+{
+       if (dialog->priv->word == NULL)
+               return;
+
+       e_spell_dictionary_learn_word (
+               dialog->priv->current_dict, dialog->priv->word, -1);
+
+       html_editor_spell_check_dialog_next (dialog);
+}
+
+static void
+html_editor_spell_check_dialog_set_dictionary (EHTMLEditorSpellCheckDialog *dialog)
+{
+       GtkComboBox *combo_box;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       ESpellDictionary *dictionary;
+
+       combo_box = GTK_COMBO_BOX (dialog->priv->dictionary_combo);
+       gtk_combo_box_get_active_iter (combo_box, &iter);
+       model = gtk_combo_box_get_model (combo_box);
+
+       gtk_tree_model_get (model, &iter, 1, &dictionary, -1);
+
+       dialog->priv->current_dict = dictionary;
+
+       /* Update suggestions */
+       html_editor_spell_check_dialog_set_word (dialog, dialog->priv->word);
+}
+
+static void
+html_editor_spell_check_dialog_show (GtkWidget *widget)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSpellCheckDialog *dialog;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+
+       dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (widget);
+
+       g_free (dialog->priv->word);
+       dialog->priv->word = NULL;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       dialog->priv->selection = webkit_dom_dom_window_get_selection (window);
+
+       /* Select the first word or quit */
+       if (html_editor_spell_check_dialog_next (dialog)) {
+               GTK_WIDGET_CLASS (e_html_editor_spell_check_dialog_parent_class)->
+                       show (widget);
+       }
+}
+
+static void
+html_editor_spell_check_dialog_finalize (GObject *object)
+{
+       EHTMLEditorSpellCheckDialogPrivate *priv;
+
+       priv = E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE (object);
+
+       g_free (priv->word);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)->
+               finalize (object);
+}
+
+static void
+html_editor_spell_check_dialog_constructed (GObject *object)
+{
+       EHTMLEditorSpellCheckDialog *dialog;
+
+       /* Chain up to parent's constructed() method. */
+       G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)->
+               constructed (object);
+
+       dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (object);
+       e_html_editor_spell_check_dialog_update_dictionaries (dialog);
+}
+
+static void
+e_html_editor_spell_check_dialog_class_init (EHTMLEditorSpellCheckDialogClass *class)
+{
+       GtkWidgetClass *widget_class;
+       GObjectClass *object_class;
+
+       g_type_class_add_private (
+               class, sizeof (EHTMLEditorSpellCheckDialogPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->finalize = html_editor_spell_check_dialog_finalize;
+       object_class->constructed = html_editor_spell_check_dialog_constructed;
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->show = html_editor_spell_check_dialog_show;
+}
+
+static void
+e_html_editor_spell_check_dialog_init (EHTMLEditorSpellCheckDialog *dialog)
+{
+       GtkWidget *widget;
+       GtkGrid *main_layout;
+       GtkListStore *store;
+       GtkTreeViewColumn *column;
+       GtkCellRenderer *renderer;
+
+       dialog->priv = E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE (dialog);
+
+       main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+       /* == Suggestions == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Suggestions</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 0, 2, 1);
+       dialog->priv->suggestion_label = widget;
+
+       /* Tree view */
+       widget = gtk_tree_view_new ();
+       gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget), FALSE);
+       gtk_widget_set_vexpand (widget, TRUE);
+       gtk_widget_set_hexpand (widget, TRUE);
+       dialog->priv->tree_view = widget;
+
+       /* Column */
+       column = gtk_tree_view_column_new_with_attributes (
+               "", gtk_cell_renderer_text_new (), "text", 0, NULL);
+       gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
+
+       /* Store */
+       store = gtk_list_store_new (1, G_TYPE_STRING);
+       gtk_tree_view_set_model (
+               GTK_TREE_VIEW (widget), GTK_TREE_MODEL (store));
+
+       /* Scrolled Window */
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       gtk_widget_set_size_request (widget, 150, -1);
+       gtk_scrolled_window_set_policy (
+               GTK_SCROLLED_WINDOW (widget),
+               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_shadow_type (
+               GTK_SCROLLED_WINDOW (widget),
+               GTK_SHADOW_ETCHED_IN);
+       gtk_container_add (GTK_CONTAINER (widget), dialog->priv->tree_view);
+       gtk_grid_attach (main_layout, widget, 0, 1, 1, 5);
+
+       /* Replace */
+       widget = gtk_button_new_with_mnemonic (_("Replace"));
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+                       GTK_STOCK_CONVERT, GTK_ICON_SIZE_BUTTON));
+       gtk_grid_attach (main_layout, widget, 1, 1, 1, 1);
+       dialog->priv->replace_button = widget;
+
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_spell_check_dialog_replace), dialog);
+
+       /* Replace All */
+       widget = gtk_button_new_with_mnemonic (_("Replace All"));
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+                       GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON));
+       gtk_grid_attach (main_layout, widget, 1, 2, 1, 1);
+       dialog->priv->replace_all_button = widget;
+
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_spell_check_dialog_replace_all), dialog);
+
+       /* Ignore */
+       widget = gtk_button_new_with_mnemonic (_("Ignore"));
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+                       GTK_STOCK_CLEAR, GTK_ICON_SIZE_BUTTON));
+       gtk_grid_attach (main_layout, widget, 1, 3, 1, 1);
+       dialog->priv->ignore_button = widget;
+
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_spell_check_dialog_ignore), dialog);
+
+       /* Skip */
+       widget = gtk_button_new_with_mnemonic (_("Skip"));
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+                       GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON));
+       gtk_grid_attach (main_layout, widget, 1, 4, 1, 1);
+       dialog->priv->skip_button = widget;
+
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_spell_check_dialog_next), dialog);
+
+       /* Back */
+       widget = gtk_button_new_with_mnemonic (_("Back"));
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+                       GTK_STOCK_GO_BACK, GTK_ICON_SIZE_BUTTON));
+       gtk_grid_attach (main_layout, widget, 1, 5, 1, 1);
+
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_spell_check_dialog_prev), dialog);
+
+       /* Dictionary label */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Dictionary</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0);
+       gtk_grid_attach (main_layout, widget, 0, 6, 2, 1);
+
+       /* Dictionaries combo */
+       widget = gtk_combo_box_new ();
+       gtk_grid_attach (main_layout, widget, 0, 7, 1, 1);
+       dialog->priv->dictionary_combo = widget;
+
+       renderer = gtk_cell_renderer_text_new ();
+       gtk_cell_layout_pack_start (
+               GTK_CELL_LAYOUT (widget), renderer, TRUE);
+       gtk_cell_layout_add_attribute (
+               GTK_CELL_LAYOUT (widget), renderer, "text", 0);
+
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_spell_check_dialog_set_dictionary), dialog);
+
+       /* Add Word button */
+       widget = gtk_button_new_with_mnemonic (_("Add Word"));
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+                       GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON));
+       gtk_grid_attach (main_layout, widget, 1, 7, 1, 1);
+       dialog->priv->add_word_button = widget;
+
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (html_editor_spell_check_dialog_learn), dialog);
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_spell_check_dialog_new (EHTMLEditor *editor)
+{
+       return g_object_new (
+               E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG,
+               "editor", editor,
+               "title", N_("Spell Checking"),
+               NULL);
+}
+
+void
+e_html_editor_spell_check_dialog_update_dictionaries (EHTMLEditorSpellCheckDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       ESpellChecker *spell_checker;
+       GtkComboBox *combo_box;
+       GtkListStore *store;
+       GQueue queue = G_QUEUE_INIT;
+       gchar **languages;
+       guint n_languages = 0;
+       guint ii;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG (dialog));
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       spell_checker = e_html_editor_view_get_spell_checker (view);
+
+       languages = e_spell_checker_list_active_languages (
+               spell_checker, &n_languages);
+       for (ii = 0; ii < n_languages; ii++) {
+               ESpellDictionary *dictionary;
+
+               dictionary = e_spell_checker_ref_dictionary (
+                       spell_checker, languages[ii]);
+               if (dictionary != NULL)
+                       g_queue_push_tail (&queue, dictionary);
+               else
+                       g_warning (
+                               "%s: No '%s' dictionary found",
+                               G_STRFUNC, languages[ii]);
+       }
+       g_strfreev (languages);
+
+       /* Populate a list store for the combo box. */
+       store = gtk_list_store_new (
+               NUM_COLUMNS,
+               G_TYPE_STRING,                  /* COLUMN_NAME */
+               E_TYPE_SPELL_DICTIONARY);       /* COLUMN_DICTIONARY */
+
+       while (!g_queue_is_empty (&queue)) {
+               ESpellDictionary *dictionary;
+               GtkTreeIter iter;
+               const gchar *name;
+
+               dictionary = g_queue_pop_head (&queue);
+               name = e_spell_dictionary_get_name (dictionary);
+
+               gtk_list_store_append (store, &iter);
+               gtk_list_store_set (
+                       store, &iter,
+                       COLUMN_NAME, name,
+                       COLUMN_DICTIONARY, dictionary,
+                       -1);
+
+               g_object_unref (dictionary);
+       }
+
+       /* FIXME Try to restore selection. */
+       combo_box = GTK_COMBO_BOX (dialog->priv->dictionary_combo);
+       gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store));
+       gtk_combo_box_set_active (combo_box, 0);
+
+       g_object_unref (store);
+}
+
diff --git a/e-util/e-html-editor-spell-check-dialog.h b/e-util/e-html-editor-spell-check-dialog.h
new file mode 100644
index 0000000..4191cd9
--- /dev/null
+++ b/e-util/e-html-editor-spell-check-dialog.h
@@ -0,0 +1,73 @@
+/*
+ * e-html-editor-spell-check-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_SPELL_CHECK_DIALOG_H
+#define E_HTML_EDITOR_SPELL_CHECK_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG \
+       (e_html_editor_spell_check_dialog_get_type ())
+#define E_HTML_EDITOR_SPELL_CHECK_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG, EHTMLEditorSpellCheckDialog))
+#define E_HTML_EDITOR_SPELL_CHECK_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG, EHTMLEditorSpellCheckDialogClass))
+#define E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG))
+#define E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG))
+#define E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG, EHTMLEditorSpellCheckDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorSpellCheckDialog EHTMLEditorSpellCheckDialog;
+typedef struct _EHTMLEditorSpellCheckDialogClass EHTMLEditorSpellCheckDialogClass;
+typedef struct _EHTMLEditorSpellCheckDialogPrivate EHTMLEditorSpellCheckDialogPrivate;
+
+struct _EHTMLEditorSpellCheckDialog {
+       EHTMLEditorDialog parent;
+       EHTMLEditorSpellCheckDialogPrivate *priv;
+};
+
+struct _EHTMLEditorSpellCheckDialogClass {
+       EHTMLEditorDialogClass parent_class;
+};
+
+GType          e_html_editor_spell_check_dialog_get_type
+                                       (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_spell_check_dialog_new
+                                       (EHTMLEditor *editor);
+void           e_html_editor_spell_check_dialog_update_dictionaries
+                                       (EHTMLEditorSpellCheckDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_SPELL_CHECK_DIALOG_H */
diff --git a/e-util/e-html-editor-table-dialog.c b/e-util/e-html-editor-table-dialog.c
new file mode 100644
index 0000000..467d2a6
--- /dev/null
+++ b/e-util/e-html-editor-table-dialog.c
@@ -0,0 +1,866 @@
+/*
+ * e-html-editor-table-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-table-dialog.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-color-combo.h"
+#include "e-html-editor-utils.h"
+#include "e-image-chooser-dialog.h"
+#include "e-misc-utils.h"
+
+#define E_HTML_EDITOR_TABLE_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_TABLE_DIALOG, EHTMLEditorTableDialogPrivate))
+
+struct _EHTMLEditorTableDialogPrivate {
+       GtkWidget *rows_edit;
+       GtkWidget *columns_edit;
+
+       GtkWidget *width_edit;
+       GtkWidget *width_units;
+       GtkWidget *width_check;
+
+       GtkWidget *spacing_edit;
+       GtkWidget *padding_edit;
+       GtkWidget *border_edit;
+
+       GtkWidget *alignment_combo;
+
+       GtkWidget *background_color_button;
+       GtkWidget *background_image_button;
+       GtkWidget *image_chooser_dialog;
+
+       WebKitDOMHTMLTableElement *table_element;
+};
+
+static GdkRGBA white = { 1, 1, 1, 1 };
+
+G_DEFINE_TYPE (
+       EHTMLEditorTableDialog,
+       e_html_editor_table_dialog,
+       E_TYPE_HTML_EDITOR_DIALOG);
+
+static WebKitDOMElement *
+html_editor_table_dialog_create_table (EHTMLEditorTableDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorSelection *editor_selection;
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *table, *br, *caret, *parent, *element;
+       gint i;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       editor_selection = e_html_editor_view_get_selection (view);
+
+       document = webkit_web_view_get_dom_document (
+               WEBKIT_WEB_VIEW (view));
+
+       /* Default 3x3 table */
+       table = webkit_dom_document_create_element (document, "TABLE", NULL);
+       for (i = 0; i < 3; i++) {
+               WebKitDOMHTMLElement *row;
+               gint j;
+
+               row = webkit_dom_html_table_element_insert_row (
+                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
+
+               for (j = 0; j < 3; j++) {
+                       webkit_dom_html_table_row_element_insert_cell (
+                               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
+               }
+       }
+
+       caret = e_html_editor_selection_save_caret_position (editor_selection);
+
+       parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (caret));
+       element = caret;
+
+       while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               element = parent;
+               parent = webkit_dom_node_get_parent_element (
+                       WEBKIT_DOM_NODE (parent));
+       }
+
+       br = webkit_dom_document_create_element (document, "BR", NULL);
+       webkit_dom_node_insert_before (
+               WEBKIT_DOM_NODE (parent),
+               WEBKIT_DOM_NODE (br),
+               webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
+               NULL);
+
+       /* Insert the table into body below the caret */
+       webkit_dom_node_insert_before (
+               WEBKIT_DOM_NODE (parent),
+               WEBKIT_DOM_NODE (table),
+               webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
+               NULL);
+
+       e_html_editor_selection_clear_caret_position_marker (editor_selection);
+
+       e_html_editor_view_set_changed (view, TRUE);
+
+       return table;
+}
+
+static void
+html_editor_table_dialog_set_row_count (EHTMLEditorTableDialog *dialog)
+{
+       WebKitDOMHTMLCollection *rows;
+       gulong ii, current_count, expected_count;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
+       current_count = webkit_dom_html_collection_get_length (rows);
+       expected_count = gtk_spin_button_get_value (
+                               GTK_SPIN_BUTTON (dialog->priv->rows_edit));
+
+       if (current_count < expected_count) {
+               for (ii = 0; ii < expected_count - current_count; ii++) {
+                       webkit_dom_html_table_element_insert_row (
+                               dialog->priv->table_element, -1, NULL);
+               }
+       } else if (current_count > expected_count) {
+               for (ii = 0; ii < current_count - expected_count; ii++) {
+                       webkit_dom_html_table_element_delete_row (
+                               dialog->priv->table_element, -1, NULL);
+               }
+       }
+}
+
+static void
+html_editor_table_dialog_get_row_count (EHTMLEditorTableDialog *dialog)
+{
+       WebKitDOMHTMLCollection *rows;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
+
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->rows_edit),
+               webkit_dom_html_collection_get_length (rows));
+}
+
+static void
+html_editor_table_dialog_set_column_count (EHTMLEditorTableDialog *dialog)
+{
+       WebKitDOMHTMLCollection *rows;
+       gulong ii, row_count, expected_columns;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
+       row_count = webkit_dom_html_collection_get_length (rows);
+       expected_columns = gtk_spin_button_get_value (
+                       GTK_SPIN_BUTTON (dialog->priv->columns_edit));
+
+       for (ii = 0; ii < row_count; ii++) {
+               WebKitDOMHTMLTableRowElement *row;
+               WebKitDOMHTMLCollection *cells;
+               gulong jj, current_columns;
+
+               row = WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (
+                       webkit_dom_html_collection_item (rows, ii));
+
+               cells = webkit_dom_html_table_row_element_get_cells (row);
+               current_columns = webkit_dom_html_collection_get_length (cells);
+
+               if (current_columns < expected_columns) {
+                       for (jj = 0; jj < expected_columns - current_columns; jj++) {
+                               webkit_dom_html_table_row_element_insert_cell (
+                                       row, -1, NULL);
+                       }
+               } else if (expected_columns < current_columns) {
+                       for (jj = 0; jj < current_columns - expected_columns; jj++) {
+                               webkit_dom_html_table_row_element_delete_cell (
+                                       row, -1, NULL);
+                       }
+               }
+       }
+}
+
+static void
+html_editor_table_dialog_get_column_count (EHTMLEditorTableDialog *dialog)
+{
+       WebKitDOMHTMLCollection *rows, *columns;
+       WebKitDOMNode *row;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
+       row = webkit_dom_html_collection_item (rows, 0);
+
+       columns = webkit_dom_html_table_row_element_get_cells (
+                               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->columns_edit),
+               webkit_dom_html_collection_get_length (columns));
+}
+
+static void
+html_editor_table_dialog_set_width (EHTMLEditorTableDialog *dialog)
+{
+       gchar *width;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       if (gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->width_check))) {
+               gchar *units;
+
+               units = gtk_combo_box_text_get_active_text (
+                               GTK_COMBO_BOX_TEXT (dialog->priv->width_units));
+               width = g_strdup_printf (
+                       "%d%s",
+                       gtk_spin_button_get_value_as_int (
+                               GTK_SPIN_BUTTON (dialog->priv->width_edit)),
+                       units);
+               g_free (units);
+
+               gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE);
+               gtk_widget_set_sensitive (dialog->priv->width_units, TRUE);
+       } else {
+               width = g_strdup ("auto");
+
+               gtk_widget_set_sensitive (dialog->priv->width_edit, FALSE);
+               gtk_widget_set_sensitive (dialog->priv->width_units, FALSE);
+       }
+
+       webkit_dom_html_table_element_set_width (
+               dialog->priv->table_element, width);
+       g_free (width);
+}
+
+static void
+html_editor_table_dialog_get_width (EHTMLEditorTableDialog *dialog)
+{
+       gchar *width;
+
+       width = webkit_dom_html_table_element_get_width (dialog->priv->table_element);
+       if (!width || !*width || g_ascii_strncasecmp (width, "auto", 4) == 0) {
+               gtk_toggle_button_set_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->width_check), FALSE);
+               gtk_spin_button_set_value (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit), 100);
+               gtk_combo_box_set_active_id (
+                       GTK_COMBO_BOX (dialog->priv->width_units), "units-percent");
+       } else {
+               gint width_int = atoi (width);
+
+               gtk_toggle_button_set_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->width_check), TRUE);
+               gtk_spin_button_set_value (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit), width_int);
+               gtk_combo_box_set_active_id (
+                       GTK_COMBO_BOX (dialog->priv->width_units),
+                       ((strstr (width, "%") == NULL) ?
+                               "units-px" : "units-percent"));
+       }
+       g_free (width);
+}
+
+static void
+html_editor_table_dialog_set_alignment (EHTMLEditorTableDialog *dialog)
+{
+       g_return_if_fail (dialog->priv->table_element);
+
+       webkit_dom_html_table_element_set_align (
+               dialog->priv->table_element,
+               gtk_combo_box_get_active_id (
+                       GTK_COMBO_BOX (dialog->priv->alignment_combo)));
+}
+
+static void
+html_editor_table_dialog_get_alignment (EHTMLEditorTableDialog *dialog)
+{
+       gchar *alignment;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       alignment = webkit_dom_html_table_element_get_align (
+                       dialog->priv->table_element);
+
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->alignment_combo), alignment);
+
+       g_free (alignment);
+}
+
+static void
+html_editor_table_dialog_set_padding (EHTMLEditorTableDialog *dialog)
+{
+       gchar *padding;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       padding = g_strdup_printf (
+               "%d",
+                       gtk_spin_button_get_value_as_int (
+                               GTK_SPIN_BUTTON (dialog->priv->padding_edit)));
+
+       webkit_dom_html_table_element_set_cell_padding (
+               dialog->priv->table_element, padding);
+
+       g_free (padding);
+}
+
+static void
+html_editor_table_dialog_get_padding (EHTMLEditorTableDialog *dialog)
+{
+       gchar *padding;
+       gint padding_int;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       padding = webkit_dom_html_table_element_get_cell_padding (
+                       dialog->priv->table_element);
+       if (!padding || !*padding) {
+               padding_int = 0;
+       } else {
+               padding_int = atoi (padding);
+       }
+
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->padding_edit), padding_int);
+
+       g_free (padding);
+}
+
+static void
+html_editor_table_dialog_set_spacing (EHTMLEditorTableDialog *dialog)
+{
+       gchar *spacing;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       spacing = g_strdup_printf (
+               "%d",
+                       gtk_spin_button_get_value_as_int (
+                               GTK_SPIN_BUTTON (dialog->priv->spacing_edit)));
+
+       webkit_dom_html_table_element_set_cell_spacing (
+               dialog->priv->table_element, spacing);
+
+       g_free (spacing);
+}
+
+static void
+html_editor_table_dialog_get_spacing (EHTMLEditorTableDialog *dialog)
+{
+       gchar *spacing;
+       gint spacing_int;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       spacing = webkit_dom_html_table_element_get_cell_spacing (
+                       dialog->priv->table_element);
+       if (!spacing || !*spacing) {
+               spacing_int = 0;
+       } else {
+               spacing_int = atoi (spacing);
+       }
+
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->spacing_edit), spacing_int);
+
+       g_free (spacing);
+}
+
+static void
+html_editor_table_dialog_set_border (EHTMLEditorTableDialog *dialog)
+{
+       gchar *border;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       border = g_strdup_printf (
+               "%d",
+                       gtk_spin_button_get_value_as_int (
+                               GTK_SPIN_BUTTON (dialog->priv->border_edit)));
+
+       webkit_dom_html_table_element_set_border (
+               dialog->priv->table_element, border);
+
+       g_free (border);
+}
+
+static void
+html_editor_table_dialog_get_border (EHTMLEditorTableDialog *dialog)
+{
+       gchar *border;
+       gint border_int;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       border = webkit_dom_html_table_element_get_border (
+                       dialog->priv->table_element);
+       if (!border || !*border) {
+               border_int = 0;
+       } else {
+               border_int = atoi (border);
+       }
+
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->border_edit), border_int);
+
+       g_free (border);
+}
+
+static void
+html_editor_table_dialog_set_background_color (EHTMLEditorTableDialog *dialog)
+{
+       gchar *color;
+       GdkRGBA rgba;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       e_color_combo_get_current_color (
+               E_COLOR_COMBO (dialog->priv->background_color_button), &rgba);
+       color = g_strdup_printf (
+                       "#%06x", e_rgba_to_value (&rgba));
+
+       webkit_dom_html_table_element_set_bg_color (
+               dialog->priv->table_element, color);
+
+       g_free (color);
+}
+
+static void
+html_editor_table_dialog_get_background_color (EHTMLEditorTableDialog *dialog)
+{
+       gchar *color;
+       GdkRGBA rgba;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       color = webkit_dom_html_table_element_get_bg_color (
+                       dialog->priv->table_element);
+
+       gdk_rgba_parse (&rgba, color);
+
+       e_color_combo_set_current_color (
+               E_COLOR_COMBO (dialog->priv->background_color_button), &rgba);
+
+       g_free (color);
+}
+
+static void
+html_editor_table_dialog_set_background_image (EHTMLEditorTableDialog *dialog)
+{
+       const gchar *filename;
+
+       g_return_if_fail (dialog->priv->table_element);
+
+       filename = gtk_file_chooser_get_filename (
+                       GTK_FILE_CHOOSER (dialog->priv->background_image_button));
+
+       if (filename) {
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (dialog->priv->table_element),
+                       "background", filename, NULL);
+       } else {
+               webkit_dom_element_remove_attribute (
+                       WEBKIT_DOM_ELEMENT (dialog->priv->table_element),
+                       "background");
+       }
+}
+
+static void
+html_editor_table_dialog_get_background_image (EHTMLEditorTableDialog *dialog)
+{
+       g_return_if_fail (dialog->priv->table_element);
+
+       if (!webkit_dom_element_has_attribute (
+               WEBKIT_DOM_ELEMENT (dialog->priv->table_element), "background")) {
+
+               gtk_file_chooser_unselect_all (
+                       GTK_FILE_CHOOSER (dialog->priv->background_image_button));
+               return;
+       } else {
+               gchar *background;
+
+               background = webkit_dom_element_get_attribute (
+                       WEBKIT_DOM_ELEMENT (dialog->priv->table_element), "background");
+
+               gtk_file_chooser_set_filename (
+                       GTK_FILE_CHOOSER (dialog->priv->background_image_button),
+                       background);
+
+               g_free (background);
+       }
+}
+
+static void
+html_editor_table_dialog_get_values (EHTMLEditorTableDialog *dialog)
+{
+       html_editor_table_dialog_get_row_count (dialog);
+       html_editor_table_dialog_get_column_count (dialog);
+       html_editor_table_dialog_get_width (dialog);
+       html_editor_table_dialog_get_alignment (dialog);
+       html_editor_table_dialog_get_spacing (dialog);
+       html_editor_table_dialog_get_padding (dialog);
+       html_editor_table_dialog_get_border (dialog);
+       html_editor_table_dialog_get_background_color (dialog);
+       html_editor_table_dialog_get_background_image (dialog);
+}
+
+static void
+html_editor_table_dialog_reset_values (EHTMLEditorTableDialog *dialog)
+{
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->rows_edit), 3);
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->columns_edit), 3);
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->alignment_combo), "left");
+
+       gtk_toggle_button_set_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->width_check), TRUE);
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->width_edit), 100);
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->width_units), "units-percent");
+
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->spacing_edit), 2);
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->padding_edit), 1);
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->border_edit), 1);
+
+       e_color_combo_set_current_color (
+               E_COLOR_COMBO (dialog->priv->background_color_button), &white);
+       gtk_file_chooser_unselect_all (
+               GTK_FILE_CHOOSER (dialog->priv->background_image_button));
+
+       html_editor_table_dialog_set_row_count (dialog);
+       html_editor_table_dialog_set_column_count (dialog);
+       html_editor_table_dialog_set_width (dialog);
+       html_editor_table_dialog_set_alignment (dialog);
+       html_editor_table_dialog_set_spacing (dialog);
+       html_editor_table_dialog_set_padding (dialog);
+       html_editor_table_dialog_set_border (dialog);
+       html_editor_table_dialog_set_background_color (dialog);
+       html_editor_table_dialog_set_background_image (dialog);
+}
+
+static void
+html_editor_table_dialog_show (GtkWidget *widget)
+{
+       EHTMLEditorTableDialog *dialog;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *selection;
+
+       dialog = E_HTML_EDITOR_TABLE_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       selection = webkit_dom_dom_window_get_selection (window);
+       if (selection && (webkit_dom_dom_selection_get_range_count (selection) > 0)) {
+               WebKitDOMElement *table;
+               WebKitDOMRange *range;
+
+               range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+               table = e_html_editor_dom_node_find_parent_element (
+                       webkit_dom_range_get_start_container (range, NULL), "TABLE");
+
+               if (!table) {
+                       dialog->priv->table_element = WEBKIT_DOM_HTML_TABLE_ELEMENT (
+                               html_editor_table_dialog_create_table (dialog));
+                       html_editor_table_dialog_reset_values (dialog);
+               } else {
+                       dialog->priv->table_element =
+                               WEBKIT_DOM_HTML_TABLE_ELEMENT (table);
+                       html_editor_table_dialog_get_values (dialog);
+               }
+       }
+
+       /* Chain up to parent implementation */
+       GTK_WIDGET_CLASS (e_html_editor_table_dialog_parent_class)->show (widget);
+}
+
+static void
+html_editor_table_dialog_hide (GtkWidget *widget)
+{
+       EHTMLEditorTableDialogPrivate *priv;
+
+       priv = E_HTML_EDITOR_TABLE_DIALOG_GET_PRIVATE (widget);
+
+       priv->table_element = NULL;
+
+       GTK_WIDGET_CLASS (e_html_editor_table_dialog_parent_class)->hide (widget);
+}
+
+static void
+e_html_editor_table_dialog_class_init (EHTMLEditorTableDialogClass *class)
+{
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorTableDialogPrivate));
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->show = html_editor_table_dialog_show;
+       widget_class->hide = html_editor_table_dialog_hide;
+}
+
+static void
+e_html_editor_table_dialog_init (EHTMLEditorTableDialog *dialog)
+{
+       GtkGrid *main_layout, *grid;
+       GtkWidget *widget;
+       GtkFileFilter *file_filter;
+
+       dialog->priv = E_HTML_EDITOR_TABLE_DIALOG_GET_PRIVATE (dialog);
+
+       main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+       /* == General == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>General</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 1, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Rows */
+       widget = gtk_image_new_from_icon_name ("stock_select-row", GTK_ICON_SIZE_BUTTON);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       widget = gtk_spin_button_new_with_range (1, G_MAXINT, 1);
+       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+       gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_table_dialog_set_row_count), dialog);
+       dialog->priv->rows_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Rows:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->rows_edit);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+
+       /* Columns */
+       widget = gtk_image_new_from_icon_name ("stock_select-column", GTK_ICON_SIZE_BUTTON);
+       gtk_grid_attach (grid, widget, 3, 0, 1, 1);
+
+       widget = gtk_spin_button_new_with_range (1, G_MAXINT, 1);
+       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+       gtk_grid_attach (grid, widget, 5, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_table_dialog_set_column_count), dialog);
+       dialog->priv->columns_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("C_olumns:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->columns_edit);
+       gtk_grid_attach (grid, widget, 4, 0, 1, 1);
+
+       /* == Layout == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Layout</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Width */
+       widget = gtk_check_button_new_with_mnemonic (_("_Width:"));
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_table_dialog_set_width), dialog);
+       dialog->priv->width_check = widget;
+
+       widget = gtk_spin_button_new_with_range (1, 100, 1);
+       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_table_dialog_set_width), dialog);
+       dialog->priv->width_edit = widget;
+
+       widget = gtk_combo_box_text_new ();
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%");
+       gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_table_dialog_set_width), dialog);
+       dialog->priv->width_units = widget;
+
+       /* Spacing */
+       widget = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
+       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+       gtk_grid_attach (grid, widget, 5, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_table_dialog_set_spacing), dialog);
+       dialog->priv->spacing_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Spacing:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->spacing_edit);
+       gtk_grid_attach (grid, widget, 4, 0, 1, 1);
+
+       widget = gtk_label_new ("px");
+       gtk_grid_attach (grid, widget, 6, 0, 1, 1);
+
+       /* Padding */
+       widget = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
+       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+       gtk_grid_attach (grid, widget, 5, 1, 1, 1);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_table_dialog_set_padding), dialog);
+       dialog->priv->padding_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Padding:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->padding_edit);
+       gtk_grid_attach (grid, widget, 4, 1, 1, 1);
+
+       widget = gtk_label_new ("px");
+       gtk_grid_attach (grid, widget, 6, 1, 1, 1);
+
+       /* Border */
+       widget = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
+       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
+       gtk_grid_attach (grid, widget, 5, 2, 1, 1);
+       g_signal_connect_swapped (
+               widget, "value-changed",
+               G_CALLBACK (html_editor_table_dialog_set_border), dialog);
+       dialog->priv->border_edit = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Border:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->border_edit);
+       gtk_grid_attach (grid, widget, 4, 2, 1, 1);
+
+       widget = gtk_label_new ("px");
+       gtk_grid_attach (grid, widget, 6, 2, 1, 1);
+
+       /* Alignment */
+       widget = gtk_combo_box_text_new ();
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "left", _("Left"));
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "center", _("Center"));
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "right", _("Right"));
+       gtk_grid_attach (grid, widget, 1, 1, 2, 1);
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_table_dialog_set_alignment), dialog);
+       dialog->priv->alignment_combo = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Alignment:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->alignment_combo);
+       gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+       /* == Background == */
+       widget = gtk_label_new ("");
+       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Background</b>"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 4, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 5, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       /* Color */
+       widget = e_color_combo_new ();
+       e_color_combo_set_default_color (E_COLOR_COMBO (widget), &white);
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "notify::current-color",
+               G_CALLBACK (html_editor_table_dialog_set_background_color), dialog);
+       dialog->priv->background_color_button = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Color:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (
+               GTK_LABEL (widget), dialog->priv->background_color_button);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       /* Image */
+       widget = e_image_chooser_dialog_new (
+                       _("Choose Background Image"),
+                       GTK_WINDOW (dialog));
+       dialog->priv->image_chooser_dialog = widget;
+
+       file_filter = gtk_file_filter_new ();
+       gtk_file_filter_set_name (file_filter, _("Images"));
+       gtk_file_filter_add_mime_type (file_filter, "image/*");
+
+       widget = gtk_file_chooser_button_new_with_dialog (
+                       dialog->priv->image_chooser_dialog);
+       gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), file_filter);
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+       g_signal_connect_swapped (
+               widget, "file-set",
+               G_CALLBACK (html_editor_table_dialog_set_background_image), dialog);
+       dialog->priv->background_image_button = widget;
+
+       widget =gtk_label_new_with_mnemonic (_("Image:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (
+               GTK_LABEL (widget), dialog->priv->background_image_button);
+       gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_table_dialog_new (EHTMLEditor *editor)
+{
+       return GTK_WIDGET (
+               g_object_new (
+                       E_TYPE_HTML_EDITOR_TABLE_DIALOG,
+                       "editor", editor,
+                       "title", N_("Table Properties"),
+                       NULL));
+}
+
diff --git a/e-util/e-html-editor-table-dialog.h b/e-util/e-html-editor-table-dialog.h
new file mode 100644
index 0000000..70c790d
--- /dev/null
+++ b/e-util/e-html-editor-table-dialog.h
@@ -0,0 +1,69 @@
+/*
+ * e-html-editor-table-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_TABLE_DIALOG_H
+#define E_HTML_EDITOR_TABLE_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_TABLE_DIALOG \
+       (e_html_editor_table_dialog_get_type ())
+#define E_HTML_EDITOR_TABLE_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_TABLE_DIALOG, EHTMLEditorTableDialog))
+#define E_HTML_EDITOR_TABLE_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_TABLE_DIALOG, EHTMLEditorTableDialogClass))
+#define E_IS_HTML_EDITOR_TABLE_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_TABLE_DIALOG))
+#define E_IS_HTML_EDITOR_TABLE_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_TABLE_DIALOG))
+#define E_HTML_EDITOR_TABLE_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_TABLE_DIALOG, EHTMLEditorTableDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorTableDialog EHTMLEditorTableDialog;
+typedef struct _EHTMLEditorTableDialogClass EHTMLEditorTableDialogClass;
+typedef struct _EHTMLEditorTableDialogPrivate EHTMLEditorTableDialogPrivate;
+
+struct _EHTMLEditorTableDialog {
+       EHTMLEditorDialog parent;
+       EHTMLEditorTableDialogPrivate *priv;
+};
+
+struct _EHTMLEditorTableDialogClass {
+       EHTMLEditorDialogClass parent_class;
+};
+
+GType          e_html_editor_table_dialog_get_type     (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_table_dialog_new  (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_TABLE_DIALOG_H */
diff --git a/e-util/e-html-editor-text-dialog.c b/e-util/e-html-editor-text-dialog.c
new file mode 100644
index 0000000..2db792c
--- /dev/null
+++ b/e-util/e-html-editor-text-dialog.c
@@ -0,0 +1,298 @@
+/*
+ * e-html-editor-text-dialog.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-text-dialog.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-color-combo.h"
+
+#define E_HTML_EDITOR_TEXT_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_TEXT_DIALOG, EHTMLEditorTextDialogPrivate))
+
+struct _EHTMLEditorTextDialogPrivate {
+       GtkWidget *bold_check;
+       GtkWidget *italic_check;
+       GtkWidget *underline_check;
+       GtkWidget *strikethrough_check;
+
+       GtkWidget *color_check;
+       GtkWidget *size_check;
+};
+
+G_DEFINE_TYPE (
+       EHTMLEditorTextDialog,
+       e_html_editor_text_dialog,
+       E_TYPE_HTML_EDITOR_DIALOG);
+
+static void
+html_editor_text_dialog_set_bold (EHTMLEditorTextDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       e_html_editor_selection_set_bold (
+               selection,
+               gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->bold_check)));
+}
+
+static void
+html_editor_text_dialog_set_italic (EHTMLEditorTextDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       e_html_editor_selection_set_italic (
+               selection,
+               gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->italic_check)));
+}
+
+static void
+html_editor_text_dialog_set_underline (EHTMLEditorTextDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       e_html_editor_selection_set_underline (
+               selection,
+               gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->underline_check)));
+}
+
+static void
+html_editor_text_dialog_set_strikethrough (EHTMLEditorTextDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       e_html_editor_selection_set_strikethrough (
+               selection,
+               gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->strikethrough_check)));
+}
+
+static void
+html_editor_text_dialog_set_color (EHTMLEditorTextDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+       GdkRGBA rgba;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       e_color_combo_get_current_color (
+               E_COLOR_COMBO (dialog->priv->color_check), &rgba);
+       e_html_editor_selection_set_font_color (selection, &rgba);
+}
+
+static void
+html_editor_text_dialog_set_size (EHTMLEditorTextDialog *dialog)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+       gint size;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+       size = gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->size_check));
+
+       e_html_editor_selection_set_font_size (selection, size + 1);
+}
+
+static void
+html_editor_text_dialog_show (GtkWidget *widget)
+{
+       EHTMLEditorTextDialog *dialog;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+       GdkRGBA rgba;
+
+       dialog = E_HTML_EDITOR_TEXT_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       gtk_toggle_button_set_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->bold_check),
+               e_html_editor_selection_is_bold (selection));
+       gtk_toggle_button_set_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->italic_check),
+               e_html_editor_selection_is_italic (selection));
+       gtk_toggle_button_set_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->underline_check),
+               e_html_editor_selection_is_underline (selection));
+       gtk_toggle_button_set_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->strikethrough_check),
+               e_html_editor_selection_is_strikethrough (selection));
+
+       gtk_combo_box_set_active (
+               GTK_COMBO_BOX (dialog->priv->size_check),
+               e_html_editor_selection_get_font_size (selection));
+
+       e_html_editor_selection_get_font_color (selection, &rgba);
+       e_color_combo_set_current_color (
+               E_COLOR_COMBO (dialog->priv->color_check), &rgba);
+
+       GTK_WIDGET_CLASS (e_html_editor_text_dialog_parent_class)->show (widget);
+}
+
+static void
+e_html_editor_text_dialog_class_init (EHTMLEditorTextDialogClass *class)
+{
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorTextDialogPrivate));
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->show = html_editor_text_dialog_show;
+}
+
+static void
+e_html_editor_text_dialog_init (EHTMLEditorTextDialog *dialog)
+{
+       GtkGrid *main_layout;
+       GtkWidget *widget;
+
+       dialog->priv = E_HTML_EDITOR_TEXT_DIALOG_GET_PRIVATE (dialog);
+
+       main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
+
+       /* Bold */
+       widget = gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_BUTTON);
+       gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
+
+       widget = gtk_check_button_new_with_mnemonic (_("_Bold"));
+       gtk_grid_attach (main_layout, widget, 1, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_text_dialog_set_bold), dialog);
+       dialog->priv->bold_check = widget;
+
+       /* Italic */
+       widget = gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_BUTTON);
+       gtk_grid_attach (main_layout, widget, 0, 1, 1, 1);
+
+       widget = gtk_check_button_new_with_mnemonic (_("_Italic"));
+       gtk_grid_attach (main_layout, widget, 1, 1, 1, 1);
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_text_dialog_set_italic), dialog);
+       dialog->priv->italic_check = widget;
+
+       /* Underline */
+       widget = gtk_image_new_from_stock (GTK_STOCK_UNDERLINE, GTK_ICON_SIZE_BUTTON);
+       gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
+
+       widget = gtk_check_button_new_with_mnemonic (_("_Underline"));
+       gtk_grid_attach (main_layout, widget, 1, 2, 1, 1);
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_text_dialog_set_underline), dialog);
+       dialog->priv->underline_check = widget;
+
+       widget = gtk_image_new_from_stock (GTK_STOCK_STRIKETHROUGH, GTK_ICON_SIZE_BUTTON);
+       gtk_grid_attach (main_layout, widget, 0, 3, 1, 1);
+
+       widget = gtk_check_button_new_with_mnemonic (_("_Strikethrough"));
+       gtk_grid_attach (main_layout, widget, 1, 3, 1, 1);
+       g_signal_connect_swapped (
+               widget, "toggled",
+               G_CALLBACK (html_editor_text_dialog_set_strikethrough), dialog);
+       dialog->priv->strikethrough_check = widget;
+
+       /* Color */
+       widget = e_color_combo_new ();
+       gtk_grid_attach (main_layout, widget, 3, 0, 1, 1);
+       g_signal_connect_swapped (
+               widget, "notify::current-color",
+               G_CALLBACK (html_editor_text_dialog_set_color), dialog);
+       dialog->priv->color_check = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Color:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->color_check);
+       gtk_grid_attach (main_layout, widget, 2, 0, 1, 1);
+
+       /* Size */
+       widget = gtk_combo_box_text_new ();
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "minus-two", "-2");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "minus-one", "-1");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "minus-zero", "0");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "plus-one", "+1");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "plus-two", "+2");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "plus-three", "+3");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "plus-four", "+4");
+       gtk_grid_attach (main_layout, widget, 3, 1, 1, 1);
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (html_editor_text_dialog_set_size), dialog);
+       dialog->priv->size_check = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("Si_ze:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->size_check);
+       gtk_grid_attach (main_layout, widget, 2, 1, 1, 1);
+
+       gtk_widget_show_all (GTK_WIDGET (main_layout));
+}
+
+GtkWidget *
+e_html_editor_text_dialog_new (EHTMLEditor *editor)
+{
+       return GTK_WIDGET (
+               g_object_new (
+                       E_TYPE_HTML_EDITOR_TEXT_DIALOG,
+                       "editor", editor,
+                       "title", N_("Text Properties"),
+                       NULL));
+}
diff --git a/e-util/e-html-editor-text-dialog.h b/e-util/e-html-editor-text-dialog.h
new file mode 100644
index 0000000..006b780
--- /dev/null
+++ b/e-util/e-html-editor-text-dialog.h
@@ -0,0 +1,69 @@
+/*
+ * e-html-editor-text-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_TEXT_DIALOG_H
+#define E_HTML_EDITOR_TEXT_DIALOG_H
+
+#include <e-util/e-html-editor-dialog.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_TEXT_DIALOG \
+       (e_html_editor_text_dialog_get_type ())
+#define E_HTML_EDITOR_TEXT_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_TEXT_DIALOG, EHTMLEditorTextDialog))
+#define E_HTML_EDITOR_TEXT_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_TEXT_DIALOG, EHTMLEditorTextDialogClass))
+#define E_IS_HTML_EDITOR_TEXT_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_TEXT_DIALOG))
+#define E_IS_HTML_EDITOR_TEXT_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_TEXT_DIALOG))
+#define E_HTML_EDITOR_TEXT_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_TEXT_DIALOG, EHTMLEditorTextDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorTextDialog EHTMLEditorTextDialog;
+typedef struct _EHTMLEditorTextDialogClass EHTMLEditorTextDialogClass;
+typedef struct _EHTMLEditorTextDialogPrivate EHTMLEditorTextDialogPrivate;
+
+struct _EHTMLEditorTextDialog {
+       EHTMLEditorDialog parent;
+       EHTMLEditorTextDialogPrivate *priv;
+};
+
+struct _EHTMLEditorTextDialogClass {
+       EHTMLEditorDialogClass parent_class;
+};
+
+GType          e_html_editor_text_dialog_get_type      (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_text_dialog_new   (EHTMLEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_TEXT_DIALOG_H */
diff --git a/e-util/e-html-editor-utils.c b/e-util/e-html-editor-utils.c
new file mode 100644
index 0000000..2807ea9
--- /dev/null
+++ b/e-util/e-html-editor-utils.c
@@ -0,0 +1,116 @@
+/*
+ * e-html-editor-utils.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-utils.h"
+#include <string.h>
+
+/**
+ * e_html_editor_dom_node_find_parent_element:
+ * @node: Start node
+ * @tagname: Tag name of element to search
+ *
+ * Recursively searches for first occurance of element with given @tagname
+ * that is parent of given @node.
+ *
+ * Returns: A #WebKitDOMElement with @tagname representing parent of @node or
+ * @NULL when @node has no parent with given @tagname. When @node matches @tagname,
+ * then the @node is returned.
+ */
+WebKitDOMElement *
+e_html_editor_dom_node_find_parent_element (WebKitDOMNode *node,
+                                            const gchar *tagname)
+{
+       gint taglen = strlen (tagname);
+
+       while (node) {
+
+               if (WEBKIT_DOM_IS_ELEMENT (node)) {
+                       gchar *node_tagname;
+
+                       node_tagname = webkit_dom_element_get_tag_name (
+                                               WEBKIT_DOM_ELEMENT (node));
+
+                       if (node_tagname &&
+                           (strlen (node_tagname) == taglen) &&
+                           (g_ascii_strncasecmp (node_tagname, tagname, taglen) == 0)) {
+                               g_free (node_tagname);
+                               return WEBKIT_DOM_ELEMENT (node);
+                       }
+
+                       g_free (node_tagname);
+               }
+
+               node = WEBKIT_DOM_NODE (webkit_dom_node_get_parent_element (node));
+       }
+
+       return NULL;
+}
+
+/**
+ * e_html_editor_dom_node_find_child_element:
+ * @node: Start node
+ * @tagname: Tag name of element to search.
+ *
+ * Recursively searches for first occurence of element with given @tagname that
+ * is a child of @node.
+ *
+ * Returns: A #WebKitDOMElement with @tagname representing a child of @node or
+ * @NULL when @node has no child with given @tagname. When @node matches @tagname,
+ * then the @node is returned.
+ */
+WebKitDOMElement *
+e_html_editor_dom_node_find_child_element (WebKitDOMNode *node,
+                                           const gchar *tagname)
+{
+       WebKitDOMNode *start_node = node;
+       gint taglen = strlen (tagname);
+
+       do {
+               if (WEBKIT_DOM_IS_ELEMENT (node)) {
+                       gchar *node_tagname;
+
+                       node_tagname = webkit_dom_element_get_tag_name (
+                                       WEBKIT_DOM_ELEMENT (node));
+
+                       if (node_tagname &&
+                           (strlen (node_tagname) == taglen) &&
+                           (g_ascii_strncasecmp (node_tagname, tagname, taglen) == 0)) {
+                               g_free (node_tagname);
+                               return WEBKIT_DOM_ELEMENT (node);
+                       }
+
+                       g_free (node_tagname);
+               }
+
+               if (webkit_dom_node_has_child_nodes (node)) {
+                       node = webkit_dom_node_get_first_child (node);
+               } else if (webkit_dom_node_get_next_sibling (node)) {
+                       node = webkit_dom_node_get_next_sibling (node);
+               } else {
+                       node = webkit_dom_node_get_parent_node (node);
+               }
+       } while (!webkit_dom_node_is_same_node (node, start_node));
+
+       return NULL;
+}
diff --git a/e-util/e-html-editor-utils.h b/e-util/e-html-editor-utils.h
new file mode 100644
index 0000000..7331a87
--- /dev/null
+++ b/e-util/e-html-editor-utils.h
@@ -0,0 +1,44 @@
+/*
+ * e-html-editor-utils.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_UTILS_H
+#define E_HTML_EDITOR_UTILS_H
+
+#include <webkit/webkitdom.h>
+
+G_BEGIN_DECLS
+
+WebKitDOMElement *
+               e_html_editor_dom_node_find_parent_element
+                                               (WebKitDOMNode *node,
+                                                const gchar *tagname);
+
+WebKitDOMElement *
+               e_html_editor_dom_node_find_child_element
+                                               (WebKitDOMNode *node,
+                                                const gchar *tagname);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_UTILS_H */
diff --git a/e-util/e-html-editor-view.c b/e-util/e-html-editor-view.c
new file mode 100644
index 0000000..21cadb3
--- /dev/null
+++ b/e-util/e-html-editor-view.c
@@ -0,0 +1,6303 @@
+/*
+ * e-html-editor-view.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-html-editor-view.h"
+#include "e-html-editor.h"
+#include "e-emoticon-chooser.h"
+
+#include <e-util/e-util.h>
+#include <e-util/e-marshal.h>
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+
+#define E_HTML_EDITOR_VIEW_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorViewPrivate))
+
+#define UNICODE_ZERO_WIDTH_SPACE "\xe2\x80\x8b"
+
+#define URL_PATTERN \
+       "((([A-Za-z]{3,9}:(?:\\/\\/)?)(?:[\\-;:&=\\+\\$,\\w]+@)?" \
+       "[A-Za-z0-9\\.\\-]+|(?:www\\.|[\\-;:&=\\+\\$,\\w]+@)" \
+       "[A-Za-z0-9\\.\\-]+)((?:\\/[\\+~%\\/\\.\\w\\-]*)?\\?" \
+       "?(?:[\\-\\+=&;% \\ \\w]*)#?(?:[\\.\\!\\/\\\\w]*))?)"
+
+#define URL_PATTERN_SPACE URL_PATTERN "\\s"
+
+#define QUOTE_SYMBOL ">"
+
+/* Keep synchronized with the same value in EHTMLEditorSelection */
+#define SPACES_PER_LIST_LEVEL 8
+
+/**
+ * EHTMLEditorView:
+ *
+ * The #EHTMLEditorView is a WebKit-based rich text editor. The view itself
+ * only provides means to configure global behavior of the editor. To work
+ * with the actual content, current cursor position or current selection,
+ * use #EHTMLEditorSelection object.
+ */
+
+struct _EHTMLEditorViewPrivate {
+       gint changed            : 1;
+       gint inline_spelling    : 1;
+       gint magic_links        : 1;
+       gint magic_smileys      : 1;
+       gint can_copy           : 1;
+       gint can_cut            : 1;
+       gint can_paste          : 1;
+       gint can_redo           : 1;
+       gint can_undo           : 1;
+       gint reload_in_progress : 1;
+       gint html_mode          : 1;
+
+       EHTMLEditorSelection *selection;
+
+       WebKitDOMElement *element_under_mouse;
+
+       GHashTable *inline_images;
+
+       GSettings *font_settings;
+       GSettings *aliasing_settings;
+
+       gboolean convertor_insert;
+
+       WebKitWebView *convertor_web_view;
+};
+
+enum {
+       PROP_0,
+       PROP_CAN_COPY,
+       PROP_CAN_CUT,
+       PROP_CAN_PASTE,
+       PROP_CAN_REDO,
+       PROP_CAN_UNDO,
+       PROP_CHANGED,
+       PROP_HTML_MODE,
+       PROP_INLINE_SPELLING,
+       PROP_MAGIC_LINKS,
+       PROP_MAGIC_SMILEYS,
+       PROP_SPELL_CHECKER
+};
+
+enum {
+       POPUP_EVENT,
+       PASTE_PRIMARY_CLIPBOARD,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static CamelDataCache *emd_global_http_cache = NULL;
+
+G_DEFINE_TYPE_WITH_CODE (
+       EHTMLEditorView,
+       e_html_editor_view,
+       WEBKIT_TYPE_WEB_VIEW,
+       G_IMPLEMENT_INTERFACE (
+               E_TYPE_EXTENSIBLE, NULL))
+
+static WebKitDOMRange *
+html_editor_view_get_dom_range (EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *selection;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       selection = webkit_dom_dom_window_get_selection (window);
+
+       if (webkit_dom_dom_selection_get_range_count (selection) < 1) {
+               return NULL;
+       }
+
+       return webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+}
+
+static void
+html_editor_view_user_changed_contents_cb (EHTMLEditorView *view,
+                                           gpointer user_data)
+{
+       WebKitWebView *web_view;
+       gboolean can_redo, can_undo;
+
+       web_view = WEBKIT_WEB_VIEW (view);
+
+       e_html_editor_view_set_changed (view, TRUE);
+
+       can_redo = webkit_web_view_can_redo (web_view);
+       if (view->priv->can_redo != can_redo) {
+               view->priv->can_redo = can_redo;
+               g_object_notify (G_OBJECT (view), "can-redo");
+       }
+
+       can_undo = webkit_web_view_can_undo (web_view);
+       if (view->priv->can_undo != can_undo) {
+               view->priv->can_undo = can_undo;
+               g_object_notify (G_OBJECT (view), "can-undo");
+       }
+}
+
+static void
+html_editor_view_selection_changed_cb (EHTMLEditorView *view,
+                                       gpointer user_data)
+{
+       WebKitWebView *web_view;
+       gboolean can_copy, can_cut, can_paste;
+
+       web_view = WEBKIT_WEB_VIEW (view);
+
+       /* When the webview is being (re)loaded, the document is in an
+        * inconsistant state and there is no selection, so don't propagate
+        * the signal further to EHTMLEditorSelection and others and wait until
+        * the load is finished. */
+       if (view->priv->reload_in_progress) {
+               g_signal_stop_emission_by_name (view, "selection-changed");
+               return;
+       }
+
+       can_copy = webkit_web_view_can_copy_clipboard (web_view);
+       if (view->priv->can_copy != can_copy) {
+               view->priv->can_copy = can_copy;
+               g_object_notify (G_OBJECT (view), "can-copy");
+       }
+
+       can_cut = webkit_web_view_can_cut_clipboard (web_view);
+       if (view->priv->can_cut != can_cut) {
+               view->priv->can_cut = can_cut;
+               g_object_notify (G_OBJECT (view), "can-cut");
+       }
+
+       can_paste = webkit_web_view_can_paste_clipboard (web_view);
+       if (view->priv->can_paste != can_paste) {
+               view->priv->can_paste = can_paste;
+               g_object_notify (G_OBJECT (view), "can-paste");
+       }
+}
+
+static gboolean
+html_editor_view_should_show_delete_interface_for_element (EHTMLEditorView *view,
+                                                           WebKitDOMHTMLElement *element)
+{
+       return FALSE;
+}
+
+void
+e_html_editor_view_force_spell_check_for_current_paragraph (EHTMLEditorView *view)
+{
+       EHTMLEditorSelection *selection;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMElement *caret, *parent, *element;
+       WebKitDOMRange *end_range, *actual;
+       WebKitDOMText *text;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+
+       element = webkit_dom_document_query_selector (
+               document, "body[spellcheck=true]", NULL);
+
+       if (!element)
+               return;
+
+       selection = e_html_editor_view_get_selection (view);
+       caret = e_html_editor_selection_save_caret_position (selection);
+
+       /* Block callbacks of selection-changed signal as we don't want to
+        * recount all the block format things in EHTMLEditorSelection and here as well
+        * when we are moving with caret */
+       g_signal_handlers_block_by_func (
+               view, html_editor_view_selection_changed_cb, NULL);
+       e_html_editor_selection_block_selection_changed (selection);
+
+       parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (caret));
+       element = caret;
+
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               element = parent;
+               parent = webkit_dom_node_get_parent_element (
+                       WEBKIT_DOM_NODE (parent));
+       }
+
+       /* Append some text on the end of the element */
+       text = webkit_dom_document_create_text_node (document, "-x-evo-end");
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (text), NULL);
+
+       /* Create range that's pointing on the end of this text */
+       end_range = webkit_dom_document_create_range (document);
+       webkit_dom_range_select_node_contents (
+               end_range, WEBKIT_DOM_NODE (text), NULL);
+       webkit_dom_range_collapse (end_range, FALSE, NULL);
+
+       /* Move on the beginning of the paragraph */
+       actual = webkit_dom_document_create_range (document);
+       webkit_dom_range_select_node_contents (
+               actual, WEBKIT_DOM_NODE (element), NULL);
+       webkit_dom_range_collapse (actual, TRUE, NULL);
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, actual);
+
+       /* Go through all words to spellcheck them. To avoid this we have to wait for
+        * http://www.w3.org/html/wg/drafts/html/master/editing.html#dom-forcespellcheck */
+       actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       /* We are moving forward word by word until we hit the text on the end of
+        * the paragraph that we previously inserted there */
+       while (actual && webkit_dom_range_compare_boundary_points (end_range, 2, actual, NULL) != 0) {
+               webkit_dom_dom_selection_modify (
+                       dom_selection, "move", "forward", "word");
+               actual = webkit_dom_dom_selection_get_range_at (
+                       dom_selection, 0, NULL);
+       }
+
+       /* Remove the text that we inserted on the end of the paragraph */
+       webkit_dom_node_remove_child (
+               WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (text), NULL);
+
+       /* Unblock the callbacks */
+       g_signal_handlers_unblock_by_func (
+               view, html_editor_view_selection_changed_cb, NULL);
+       e_html_editor_selection_unblock_selection_changed (selection);
+
+       e_html_editor_selection_restore_caret_position (selection);
+}
+
+static void
+move_caret_into_element (WebKitDOMDocument *document,
+                         WebKitDOMElement *element)
+{
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *window_selection;
+       WebKitDOMRange *new_range;
+
+       if (!element)
+               return;
+
+       window = webkit_dom_document_get_default_view (document);
+       window_selection = webkit_dom_dom_window_get_selection (window);
+       new_range = webkit_dom_document_create_range (document);
+
+       webkit_dom_range_select_node_contents (
+               new_range, WEBKIT_DOM_NODE (element), NULL);
+       webkit_dom_range_collapse (new_range, FALSE, NULL);
+       webkit_dom_dom_selection_remove_all_ranges (window_selection);
+       webkit_dom_dom_selection_add_range (window_selection, new_range);
+}
+
+static void
+refresh_spell_check (EHTMLEditorView *view,
+                     gboolean enable_spell_check)
+{
+       EHTMLEditorSelection *selection;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMRange *end_range, *actual;
+       WebKitDOMText *text;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+
+       /* Enable/Disable spellcheck in composer */
+       body = webkit_dom_document_get_body (document);
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (body),
+               "spellcheck",
+               enable_spell_check ? "true" : "false",
+               NULL);
+
+       selection = e_html_editor_view_get_selection (view);
+       e_html_editor_selection_save_caret_position (selection);
+
+       /* Sometimes the web view is not event focused, so we have to move caret
+        * into body */
+       if (!webkit_dom_document_get_element_by_id (document, "-x-evo-caret-position")) {
+               move_caret_into_element (
+                       document,
+                       WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)));
+               e_html_editor_selection_save_caret_position (selection);
+       }
+
+       /* Block callbacks of selection-changed signal as we don't want to
+        * recount all the block format things in EHTMLEditorSelection and here as well
+        * when we are moving with caret */
+       g_signal_handlers_block_by_func (
+               view, html_editor_view_selection_changed_cb, NULL);
+       e_html_editor_selection_block_selection_changed (selection);
+
+       /* Append some text on the end of the body */
+       text = webkit_dom_document_create_text_node (document, "-x-evo-end");
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (text), NULL);
+
+       /* Create range that's pointing on the end of this text */
+       end_range = webkit_dom_document_create_range (document);
+       webkit_dom_range_select_node_contents (
+               end_range, WEBKIT_DOM_NODE (text), NULL);
+       webkit_dom_range_collapse (end_range, FALSE, NULL);
+
+       /* Move on the beginning of the document */
+       webkit_dom_dom_selection_modify (
+               dom_selection, "move", "backward", "documentboundary");
+
+       /* Go through all words to spellcheck them. To avoid this we have to wait for
+        * http://www.w3.org/html/wg/drafts/html/master/editing.html#dom-forcespellcheck */
+       actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       /* We are moving forward word by word until we hit the text on the end of
+        * the body that we previously inserted there */
+       while (actual && webkit_dom_range_compare_boundary_points (end_range, 2, actual, NULL) != 0) {
+               webkit_dom_dom_selection_modify (
+                       dom_selection, "move", "forward", "word");
+               actual = webkit_dom_dom_selection_get_range_at (
+                       dom_selection, 0, NULL);
+       }
+
+       /* Remove the text that we inserted on the end of the body */
+       webkit_dom_node_remove_child (
+               WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (text), NULL);
+
+       /* Unblock the callbacks */
+       g_signal_handlers_unblock_by_func (
+               view, html_editor_view_selection_changed_cb, NULL);
+       e_html_editor_selection_unblock_selection_changed (selection);
+
+       e_html_editor_selection_restore_caret_position (selection);
+}
+
+void
+e_html_editor_view_turn_spell_check_off (EHTMLEditorView *view)
+{
+       refresh_spell_check (view, FALSE);
+}
+
+void
+e_html_editor_view_force_spell_check (EHTMLEditorView *view)
+{
+       refresh_spell_check (view, TRUE);
+}
+
+static void
+body_input_event_cb (WebKitDOMElement *element,
+                     WebKitDOMEvent *event,
+                     EHTMLEditorView *view)
+{
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = html_editor_view_get_dom_range (view);
+
+       e_html_editor_view_set_changed (view, TRUE);
+
+       node = webkit_dom_range_get_end_container (range, NULL);
+
+       /* After toggling monospaced format, we are using UNICODE_ZERO_WIDTH_SPACE
+        * to move caret into right space. When this callback is called it is not
+        * necassary anymore so remove it */
+       if (e_html_editor_view_get_html_mode (view)) {
+               WebKitDOMElement *parent = webkit_dom_node_get_parent_element (node);
+
+               if (parent) {
+                       WebKitDOMNode *prev_sibling;
+
+                       prev_sibling = webkit_dom_node_get_previous_sibling (
+                               WEBKIT_DOM_NODE (parent));
+
+                       if (prev_sibling && WEBKIT_DOM_IS_TEXT (prev_sibling)) {
+                               gchar *text = webkit_dom_node_get_text_content (
+                                       prev_sibling);
+
+                               if (g_strcmp0 (text, UNICODE_ZERO_WIDTH_SPACE) == 0) {
+                                       webkit_dom_node_remove_child (
+                                               webkit_dom_node_get_parent_node (
+                                                       prev_sibling),
+                                               prev_sibling,
+                                               NULL);
+                               }
+                               g_free (text);
+                       }
+
+               }
+       }
+
+       /* If text before caret includes UNICODE_ZERO_WIDTH_SPACE character, remove it */
+       if (WEBKIT_DOM_IS_TEXT (node)) {
+               gchar *text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (node));
+               glong length = g_utf8_strlen (text, -1);
+               WebKitDOMNode *parent;
+
+               /* We have to preserve empty paragraphs with just UNICODE_ZERO_WIDTH_SPACE
+                * character as when we will remove it it will collapse */
+               if (length > 1) {
+                       if (g_str_has_prefix (text, UNICODE_ZERO_WIDTH_SPACE))
+                               webkit_dom_character_data_replace_data (
+                                       WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL);
+                       else if (g_str_has_suffix (text, UNICODE_ZERO_WIDTH_SPACE))
+                               webkit_dom_character_data_replace_data (
+                                       WEBKIT_DOM_CHARACTER_DATA (node), length - 1, 1, "", NULL);
+               }
+               g_free (text);
+
+               parent = webkit_dom_node_get_parent_node (node);
+               if ((WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (parent) ||
+                   WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent)) &&
+                   !element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-paragraph")) {
+                       if (e_html_editor_view_get_html_mode (view)) {
+                               element_add_class (
+                                       WEBKIT_DOM_ELEMENT (parent), "-x-evo-paragraph");
+                       } else {
+                               e_html_editor_selection_set_paragraph_style (
+                                       e_html_editor_view_get_selection (view),
+                                       WEBKIT_DOM_ELEMENT (parent),
+                                       -1, 0, "");
+                       }
+               }
+
+               /* When new smiley is added we have to use UNICODE_HIDDEN_SPACE to set the
+                * caret position to right place. It is removed when user starts typing. But
+                * when the user will press left arrow he will move the caret into
+                * smiley wrapper. If he will start to write there we have to move the written
+                * text out of the wrapper and move caret to right place */
+               if (WEBKIT_DOM_IS_ELEMENT (parent) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-smiley-wrapper")) {
+                       WebKitDOMDocument *document;
+
+                       document = webkit_web_view_get_dom_document (
+                               WEBKIT_WEB_VIEW (view));
+
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               e_html_editor_selection_get_caret_position_node (
+                                       document),
+                               webkit_dom_node_get_next_sibling (parent),
+                               NULL);
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               node,
+                               webkit_dom_node_get_next_sibling (parent),
+                               NULL);
+                       e_html_editor_selection_restore_caret_position (
+                               e_html_editor_view_get_selection (view));
+               }
+       }
+}
+
+static void
+set_base64_to_element_attribute (EHTMLEditorView *view,
+                                 WebKitDOMElement *element,
+                                 const gchar *attribute)
+{
+       gchar *attribute_value;
+       const gchar *base64_src;
+
+       attribute_value = webkit_dom_element_get_attribute (element, attribute);
+
+       if ((base64_src = g_hash_table_lookup (view->priv->inline_images, attribute_value)) != NULL) {
+               const gchar *base64_data = strstr (base64_src, ";") + 1;
+               gchar *name;
+               glong name_length;
+
+               name_length =
+                       g_utf8_strlen (base64_src, -1) -
+                       g_utf8_strlen (base64_data, -1) - 1;
+               name = g_strndup (base64_src, name_length);
+
+               webkit_dom_element_set_attribute (element, "data-inline", "", NULL);
+               webkit_dom_element_set_attribute (element, "data-name", name, NULL);
+               webkit_dom_element_set_attribute (element, attribute, base64_data, NULL);
+
+               g_free (name);
+       }
+}
+
+static void
+change_cid_images_src_to_base64 (EHTMLEditorView *view)
+{
+       gint ii, length;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *document_element;
+       WebKitDOMNamedNodeMap *attributes;
+       WebKitDOMNodeList *list;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       document_element = webkit_dom_document_get_document_element (document);
+
+       list = webkit_dom_document_query_selector_all (document, "img[src^=\"cid:\"]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               set_base64_to_element_attribute (view, WEBKIT_DOM_ELEMENT (node), "src");
+       }
+
+       /* Namespaces */
+       attributes = webkit_dom_element_get_attributes (document_element);
+       length = webkit_dom_named_node_map_get_length (attributes);
+       for (ii = 0; ii < length; ii++) {
+               gchar *name;
+               WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+               name = webkit_dom_node_get_local_name (node);
+
+               if (g_str_has_prefix (name, "xmlns:")) {
+                       const gchar *ns = name + 6;
+                       gchar *attribute_ns = g_strconcat (ns, ":src", NULL);
+                       gchar *selector = g_strconcat ("img[", ns, "\\:src^=\"cid:\"]", NULL);
+                       gint ns_length, jj;
+
+                       list = webkit_dom_document_query_selector_all (
+                               document, selector, NULL);
+                       ns_length = webkit_dom_node_list_get_length (list);
+                       for (jj = 0; jj < ns_length; jj++) {
+                               WebKitDOMNode *node = webkit_dom_node_list_item (list, jj);
+
+                               set_base64_to_element_attribute (
+                                       view, WEBKIT_DOM_ELEMENT (node), attribute_ns);
+                       }
+
+                       g_free (attribute_ns);
+                       g_free (selector);
+               }
+               g_free (name);
+       }
+
+       list = webkit_dom_document_query_selector_all (
+               document, "[background^=\"cid:\"]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               set_base64_to_element_attribute (
+                       view, WEBKIT_DOM_ELEMENT (node), "background");
+       }
+       g_hash_table_remove_all (view->priv->inline_images);
+}
+
+/* For purpose of this function see e-mail-formatter-quote.c */
+static void
+put_body_in_citation (WebKitDOMDocument *document)
+{
+       WebKitDOMElement *cite_body = webkit_dom_document_query_selector (
+               document, "span.-x-evo-cite-body", NULL);
+
+       if (cite_body) {
+               WebKitDOMHTMLElement *body = webkit_dom_document_get_body (document);
+               gchar *inner_html, *with_citation;
+
+               webkit_dom_node_remove_child (
+                       WEBKIT_DOM_NODE (body),
+                       WEBKIT_DOM_NODE (cite_body),
+                       NULL);
+
+               inner_html = webkit_dom_html_element_get_inner_html (body);
+               with_citation = g_strconcat (
+                       "<blockquote type=\"cite\" id=\"-x-evo-main-cite\">",
+                       inner_html, "</span>", NULL);
+               webkit_dom_html_element_set_inner_html (body, with_citation, NULL);
+               g_free (inner_html);
+               g_free (with_citation);
+       }
+}
+
+/* For purpose of this function see e-mail-formatter-quote.c */
+static void
+move_elements_to_body (WebKitDOMDocument *document)
+{
+       WebKitDOMHTMLElement *body = webkit_dom_document_get_body (document);
+       WebKitDOMNodeList *list;
+       gint ii;
+
+       list = webkit_dom_document_query_selector_all (
+               document, "span.-x-evo-to-body", NULL);
+       for (ii = webkit_dom_node_list_get_length (list) - 1; ii >= 0; ii--) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               while (webkit_dom_node_has_child_nodes (node)) {
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (body),
+                               webkit_dom_node_get_first_child (node),
+                               webkit_dom_node_get_first_child (
+                                       WEBKIT_DOM_NODE (body)),
+                               NULL);
+               }
+
+               webkit_dom_node_remove_child (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (node),
+                       NULL);
+       }
+}
+
+static void
+repair_gmail_blockquotes (WebKitDOMDocument *document)
+{
+       WebKitDOMNodeList *list;
+       gint ii, length;
+
+       list = webkit_dom_document_query_selector_all (
+               document, "blockquote.gmail_quote", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "class");
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "style");
+               webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node), "type", "cite", NULL);
+       }
+}
+
+static void
+html_editor_view_load_status_changed (EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       WebKitLoadStatus status;
+
+       status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (view));
+       if (status != WEBKIT_LOAD_FINISHED)
+               return;
+
+       view->priv->reload_in_progress = FALSE;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+
+       webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (body), "style");
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-message", "", NULL);
+
+       put_body_in_citation (document);
+       move_elements_to_body (document);
+       repair_gmail_blockquotes (document);
+
+       /* Register on input event that is called when the content (body) is modified */
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (body),
+               "input",
+               G_CALLBACK (body_input_event_cb),
+               FALSE,
+               view);
+
+       if (view->priv->html_mode)
+               change_cid_images_src_to_base64 (view);
+}
+
+/* Based on original use_pictograms() from GtkHTML */
+static const gchar *emoticons_chars =
+       /*  0 */ "DO)(|/PQ*!"
+       /* 10 */ "S\0:-\0:\0:-\0"
+       /* 20 */ ":\0:;=-\"\0:;"
+       /* 30 */ "B\"|\0:-'\0:X"
+       /* 40 */ "\0:\0:-\0:\0:-"
+       /* 50 */ "\0:\0:-\0:\0:-"
+       /* 60 */ "\0:\0:\0:-\0:\0"
+       /* 70 */ ":-\0:\0:-\0:\0";
+static gint emoticons_states[] = {
+       /*  0 */  12,  17,  22,  34,  43,  48,  53,  58,  65,  70,
+       /* 10 */  75,   0, -15,  15,   0, -15,   0, -17,  20,   0,
+       /* 20 */ -17,   0, -14, -20, -14,  28,  63,   0, -14, -20,
+       /* 30 */  -3,  63, -18,   0, -12,  38,  41,   0, -12,  -2,
+       /* 40 */   0,  -4,   0, -10,  46,   0, -10,   0, -19,  51,
+       /* 50 */   0, -19,   0, -11,  56,   0, -11,   0, -13,  61,
+       /* 60 */   0, -13,   0,  -6,   0,  68,  -7,   0,  -7,   0,
+       /* 70 */ -16,  73,   0, -16,   0, -21,  78,   0, -21,   0 };
+static const gchar *emoticons_icon_names[] = {
+       "face-angel",
+       "face-angry",
+       "face-cool",
+       "face-crying",
+       "face-devilish",
+       "face-embarrassed",
+       "face-kiss",
+       "face-laugh",           /* not used */
+       "face-monkey",          /* not used */
+       "face-plain",
+       "face-raspberry",
+       "face-sad",
+       "face-sick",
+       "face-smile",
+       "face-smile-big",
+       "face-smirk",
+       "face-surprise",
+       "face-tired",
+       "face-uncertain",
+       "face-wink",
+       "face-worried"
+};
+
+static void
+html_editor_view_check_magic_links (EHTMLEditorView *view,
+                                    WebKitDOMRange *range,
+                                    gboolean include_space_by_user,
+                                    GdkEventKey *event)
+{
+       gchar *node_text;
+       gchar **urls;
+       GRegex *regex = NULL;
+       GMatchInfo *match_info;
+       gint start_pos_url, end_pos_url;
+       WebKitDOMNode *node;
+       gboolean include_space = FALSE;
+       gboolean return_pressed = FALSE;
+
+       if (event != NULL) {
+               if ((event->keyval == GDK_KEY_Return) ||
+                   (event->keyval == GDK_KEY_Linefeed) ||
+                   (event->keyval == GDK_KEY_KP_Enter)) {
+
+                       return_pressed = TRUE;
+               }
+
+               if (event->keyval == GDK_KEY_space)
+                       include_space = TRUE;
+       } else {
+               include_space = include_space_by_user;
+       }
+
+       node = webkit_dom_range_get_end_container (range, NULL);
+
+       if (return_pressed)
+               node = webkit_dom_node_get_previous_sibling (node);
+
+       if (!node)
+               return;
+
+       if (!WEBKIT_DOM_IS_TEXT (node)) {
+               if (webkit_dom_node_has_child_nodes (node))
+                       node = webkit_dom_node_get_first_child (node);
+               if (!WEBKIT_DOM_IS_TEXT (node))
+                       return;
+       }
+
+       node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node));
+       if (!node_text || !(*node_text) || !g_utf8_validate (node_text, -1, NULL))
+               return;
+
+       regex = g_regex_new (include_space ? URL_PATTERN_SPACE : URL_PATTERN, 0, 0, NULL);
+
+       if (!regex) {
+               g_free (node_text);
+               return;
+       }
+
+       g_regex_match_all (regex, node_text, G_REGEX_MATCH_NOTEMPTY, &match_info);
+       urls = g_match_info_fetch_all (match_info);
+
+       if (urls) {
+               gchar *final_url, *url_end_raw;
+               glong url_start, url_end, url_length;
+               WebKitDOMDocument *document;
+               WebKitDOMNode *url_text_node_clone;
+               WebKitDOMText *url_text_node;
+               WebKitDOMElement *anchor;
+               const gchar* url_text;
+
+               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+               if (!return_pressed)
+                       e_html_editor_selection_save_caret_position (
+                               e_html_editor_view_get_selection (view));
+
+               g_match_info_fetch_pos (match_info, 0, &start_pos_url, &end_pos_url);
+
+               /* Get start and end position of url in node's text because positions
+                * that we get from g_match_info_fetch_pos are not UTF-8 aware */
+               url_end_raw = g_strndup(node_text, end_pos_url);
+               url_end = g_utf8_strlen (url_end_raw, -1);
+
+               url_length = g_utf8_strlen (urls[0], -1);
+               url_start = url_end - url_length;
+
+               webkit_dom_text_split_text (
+                       WEBKIT_DOM_TEXT (node),
+                       include_space ? url_end - 1 : url_end,
+                       NULL);
+
+               url_text_node = webkit_dom_text_split_text (
+                       WEBKIT_DOM_TEXT (node), url_start, NULL);
+               url_text_node_clone = webkit_dom_node_clone_node (
+                       WEBKIT_DOM_NODE (url_text_node), TRUE);
+               url_text = webkit_dom_text_get_whole_text (
+                       WEBKIT_DOM_TEXT (url_text_node_clone));
+
+               final_url = g_strconcat (
+                       g_str_has_prefix (url_text, "www") ? "http://"; : "", url_text, NULL);
+
+               /* Create and prepare new anchor element */
+               anchor = webkit_dom_document_create_element (document, "A", NULL);
+
+               webkit_dom_html_element_set_inner_html (
+                       WEBKIT_DOM_HTML_ELEMENT (anchor),
+                       url_text,
+                       NULL);
+
+               webkit_dom_html_anchor_element_set_href (
+                       WEBKIT_DOM_HTML_ANCHOR_ELEMENT (anchor),
+                       final_url);
+
+               /* Insert new anchor element into document */
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (anchor),
+                       WEBKIT_DOM_NODE (url_text_node),
+                       NULL);
+
+               if (!return_pressed)
+                       e_html_editor_selection_restore_caret_position (
+                               e_html_editor_view_get_selection (view));
+
+               g_free (url_end_raw);
+               g_free (final_url);
+       } else {
+               WebKitDOMElement *parent;
+               WebKitDOMNode *prev_sibling;
+               gchar *href, *text, *url;
+               gint diff;
+               const char* text_to_append;
+               gboolean appending_to_link = FALSE;
+
+               parent = webkit_dom_node_get_parent_element (node);
+               prev_sibling = webkit_dom_node_get_previous_sibling (node);
+
+               /* If previous sibling is ANCHOR and actual text node is not beginning with
+                * space => we're appending to link */
+               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling)) {
+                       text_to_append = webkit_dom_node_get_text_content (node);
+                       if (g_strcmp0 (text_to_append, "") != 0 &&
+                               !g_unichar_isspace (g_utf8_get_char (text_to_append))) {
+
+                               appending_to_link = TRUE;
+                               parent = WEBKIT_DOM_ELEMENT (prev_sibling);
+                       }
+               }
+
+               /* If parent is ANCHOR => we're editing the link */
+               if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) && !appending_to_link) {
+                       g_match_info_free (match_info);
+                       g_regex_unref (regex);
+                       g_free (node_text);
+                       return;
+               }
+
+               /* edit only if href and description are the same */
+               href = webkit_dom_html_anchor_element_get_href (
+                       WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent));
+
+               if (appending_to_link) {
+                       gchar *inner_text;
+
+                       inner_text =
+                               webkit_dom_html_element_get_inner_text (
+                                       WEBKIT_DOM_HTML_ELEMENT (parent)),
+
+                       text = g_strconcat (inner_text, text_to_append, NULL);
+                       g_free (inner_text);
+               } else
+                       text = webkit_dom_html_element_get_inner_text (
+                                       WEBKIT_DOM_HTML_ELEMENT (parent));
+
+               if (strstr (href, "://") && !strstr (text, "://")) {
+                       url = strstr (href, "://") + 3;
+                       diff = strlen (text) - strlen (url);
+
+                       if (text [strlen (text) - 1] != '/')
+                               diff++;
+
+                       if ((g_strcmp0 (url, text) != 0 && ABS (diff) == 1) || appending_to_link) {
+                               gchar *inner_html, *protocol, *new_href;
+
+                               protocol = g_strndup (href, strstr (href, "://") - href + 3);
+                               inner_html = webkit_dom_html_element_get_inner_html (
+                                       WEBKIT_DOM_HTML_ELEMENT (parent));
+                               new_href = g_strconcat (
+                                       protocol, inner_html, appending_to_link ? text_to_append : "", NULL);
+
+                               webkit_dom_html_anchor_element_set_href (
+                                       WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent),
+                                       new_href);
+
+                               if (appending_to_link) {
+                                       gchar *tmp;
+
+                                       tmp = g_strconcat (inner_html, text_to_append, NULL);
+                                       webkit_dom_html_element_set_inner_html (
+                                               WEBKIT_DOM_HTML_ELEMENT (parent),
+                                               tmp,
+                                               NULL);
+
+                                       webkit_dom_node_remove_child (
+                                               webkit_dom_node_get_parent_node (node),
+                                               node, NULL);
+
+                                       g_free (tmp);
+                               }
+
+                               g_free (new_href);
+                               g_free (protocol);
+                               g_free (inner_html);
+                       }
+               } else {
+                       diff = strlen (text) - strlen (href);
+                       if (text [strlen (text) - 1] != '/')
+                               diff++;
+
+                       if ((g_strcmp0 (href, text) != 0 && ABS (diff) == 1) || appending_to_link) {
+                               gchar *inner_html;
+                               gchar *new_href;
+
+                               inner_html = webkit_dom_html_element_get_inner_html (
+                                       WEBKIT_DOM_HTML_ELEMENT (parent));
+                               new_href = g_strconcat (
+                                               inner_html,
+                                               appending_to_link ? text_to_append : "",
+                                               NULL);
+
+                               webkit_dom_html_anchor_element_set_href (
+                                       WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent),
+                                       new_href);
+
+                               if (appending_to_link) {
+                                       gchar *tmp;
+
+                                       tmp = g_strconcat (inner_html, text_to_append, NULL);
+                                       webkit_dom_html_element_set_inner_html (
+                                               WEBKIT_DOM_HTML_ELEMENT (parent),
+                                               tmp,
+                                               NULL);
+
+                                       webkit_dom_node_remove_child (
+                                               webkit_dom_node_get_parent_node (node),
+                                               node, NULL);
+
+                                       g_free (tmp);
+                               }
+
+                               g_free (new_href);
+                               g_free (inner_html);
+                       }
+
+               }
+               g_free (text);
+               g_free (href);
+       }
+
+       g_match_info_free (match_info);
+       g_regex_unref (regex);
+       g_free (node_text);
+}
+
+typedef struct _LoadContext LoadContext;
+
+struct _LoadContext {
+       EHTMLEditorView *view;
+       gchar *content_type;
+       gchar *name;
+       EEmoticon *emoticon;
+};
+
+static LoadContext *
+emoticon_load_context_new (EHTMLEditorView *view,
+                           EEmoticon *emoticon)
+{
+       LoadContext *load_context;
+
+       load_context = g_slice_new0 (LoadContext);
+       load_context->view = view;
+       load_context->emoticon = emoticon;
+
+       return load_context;
+}
+
+static void
+emoticon_load_context_free (LoadContext *load_context)
+{
+       g_free (load_context->content_type);
+       g_free (load_context->name);
+       g_slice_free (LoadContext, load_context);
+}
+
+static void
+emoticon_read_async_cb (GFile *file,
+                        GAsyncResult *result,
+                        LoadContext *load_context)
+{
+       EHTMLEditorView *view = load_context->view;
+       EEmoticon *emoticon = load_context->emoticon;
+       GError *error = NULL;
+       gchar *html, *node_text = NULL, *mime_type;
+       gchar *base64_encoded, *output, *data;
+       const gchar *emoticon_start;
+       GFileInputStream *input_stream;
+       GOutputStream *output_stream;
+       gssize size;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *span, *caret_position;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range;
+
+       input_stream = g_file_read_finish (file, result, &error);
+       g_return_if_fail (!error && input_stream);
+
+       output_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+
+       size = g_output_stream_splice (
+               output_stream, G_INPUT_STREAM (input_stream),
+               G_OUTPUT_STREAM_SPLICE_NONE, NULL, &error);
+
+       if (error || (size == -1))
+               goto out;
+
+       caret_position = e_html_editor_selection_save_caret_position (
+               e_html_editor_view_get_selection (view));
+
+       if (caret_position) {
+               WebKitDOMNode *parent;
+
+               parent = webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (caret_position));
+
+               /* Situation when caret is restored in body and not in paragraph */
+               if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+                       caret_position = WEBKIT_DOM_ELEMENT (
+                               webkit_dom_node_remove_child (
+                                       WEBKIT_DOM_NODE (parent),
+                                       WEBKIT_DOM_NODE (caret_position),
+                                       NULL));
+
+                       caret_position = WEBKIT_DOM_ELEMENT (
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_first_child (
+                                               WEBKIT_DOM_NODE (parent)),
+                                       WEBKIT_DOM_NODE (caret_position),
+                                       webkit_dom_node_get_first_child (
+                                               webkit_dom_node_get_first_child (
+                                                       WEBKIT_DOM_NODE (parent))),
+                                       NULL));
+               }
+       }
+
+       mime_type = g_content_type_get_mime_type (load_context->content_type);
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       range = html_editor_view_get_dom_range (view);
+       node = webkit_dom_range_get_end_container (range, NULL);
+       if (WEBKIT_DOM_IS_TEXT (node))
+               node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node));
+       span = webkit_dom_document_create_element (document, "SPAN", NULL);
+
+       data = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output_stream));
+
+       base64_encoded = g_base64_encode ((const guchar *) data, size);
+       output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL);
+
+       /* Insert span with image representation and another one with text
+        * represetation and hide/show them dependant on active composer mode */
+       /* &#8203 == UNICODE_ZERO_WIDTH_SPACE */
+       html = g_strdup_printf (
+               "<span class=\"-x-evo-smiley-wrapper -x-evo-resizable-wrapper\">"
+               "<img src=\"%s\" alt=\"%s\" x-evo-smiley=\"%s\" "
+               "class=\"-x-evo-smiley-img\" data-inline data-name=\"%s\"/>"
+               "<span class=\"-x-evo-smiley-text\" style=\"display: none;\">%s"
+               "</span></span>&#8203;",
+               output, emoticon ? emoticon->text_face : "", emoticon->icon_name,
+               load_context->name, emoticon ? emoticon->text_face : "");
+
+       span = WEBKIT_DOM_ELEMENT (
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (
+                               WEBKIT_DOM_NODE (caret_position)),
+                       WEBKIT_DOM_NODE (span),
+                       WEBKIT_DOM_NODE (caret_position),
+                       NULL));
+
+       webkit_dom_html_element_set_outer_html (
+               WEBKIT_DOM_HTML_ELEMENT (span), html, NULL);
+
+       if (node_text) {
+               emoticon_start = g_utf8_strrchr (
+                       node_text, -1, g_utf8_get_char (emoticon->text_face));
+               if (emoticon_start) {
+                       webkit_dom_character_data_delete_data (
+                               WEBKIT_DOM_CHARACTER_DATA (node),
+                               g_utf8_strlen (node_text, -1) - strlen (emoticon_start),
+                               strlen (emoticon->text_face),
+                               NULL);
+               }
+       }
+
+       e_html_editor_selection_restore_caret_position (
+               e_html_editor_view_get_selection (view));
+
+       e_html_editor_view_set_changed (view, TRUE);
+
+       g_free (html);
+       g_free (node_text);
+       g_free (base64_encoded);
+       g_free (output);
+       g_free (mime_type);
+       g_object_unref (output_stream);
+ out:
+       emoticon_load_context_free (load_context);
+}
+
+static void
+emoticon_query_info_async_cb (GFile *file,
+                              GAsyncResult *result,
+                              LoadContext *load_context)
+{
+       GError *error = NULL;
+       GFileInfo *info;
+
+       info = g_file_query_info_finish (file, result, &error);
+       g_return_if_fail (!error && info);
+
+       load_context->content_type = g_strdup (g_file_info_get_content_type (info));
+       load_context->name = g_strdup (g_file_info_get_name (info));
+
+       g_file_read_async (
+               file, G_PRIORITY_DEFAULT, NULL,
+               (GAsyncReadyCallback) emoticon_read_async_cb, load_context);
+
+       g_object_unref (info);
+}
+
+void
+e_html_editor_view_insert_smiley (EHTMLEditorView *view,
+                                  EEmoticon *emoticon)
+{
+       GFile *file;
+       gchar *filename_uri;
+       LoadContext *load_context;
+
+       filename_uri = e_emoticon_get_uri (emoticon);
+       g_return_if_fail (filename_uri != NULL);
+
+       load_context = emoticon_load_context_new (view, emoticon);
+
+       file = g_file_new_for_uri (filename_uri);
+       g_file_query_info_async (
+               file,  "standard::*", G_FILE_QUERY_INFO_NONE,
+               G_PRIORITY_DEFAULT, NULL,
+               (GAsyncReadyCallback) emoticon_query_info_async_cb, load_context);
+
+       g_free (filename_uri);
+       g_object_unref (file);
+}
+
+static void
+html_editor_view_check_magic_smileys (EHTMLEditorView *view,
+                                      WebKitDOMRange *range)
+{
+       gint pos;
+       gint state;
+       gint relative;
+       gint start;
+       gchar *node_text;
+       gunichar uc;
+       WebKitDOMNode *node;
+
+       node = webkit_dom_range_get_end_container (range, NULL);
+       if (!WEBKIT_DOM_IS_TEXT (node))
+               return;
+
+       node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node));
+       if (node_text == NULL)
+               return;
+
+       start = webkit_dom_range_get_end_offset (range, NULL) - 1;
+       pos = start;
+       state = 0;
+       while (pos >= 0) {
+               uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos));
+               relative = 0;
+               while (emoticons_chars[state + relative]) {
+                       if (emoticons_chars[state + relative] == uc)
+                               break;
+                       relative++;
+               }
+               state = emoticons_states[state + relative];
+               /* 0 .. not found, -n .. found n-th */
+               if (state <= 0)
+                       break;
+               pos--;
+       }
+
+       /* Special case needed to recognize angel and devilish. */
+       if (pos > 0 && state == -14) {
+               uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1));
+               if (uc == 'O') {
+                       state = -1;
+                       pos--;
+               } else if (uc == '>') {
+                       state = -5;
+                       pos--;
+               }
+       }
+
+       if (state < 0) {
+               const EEmoticon *emoticon;
+
+               if (pos > 0) {
+                       uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1));
+                       if (!g_unichar_isspace (uc)) {
+                               g_free (node_text);
+                               return;
+                       }
+               }
+
+               emoticon = (e_emoticon_chooser_lookup_emoticon (
+                       emoticons_icon_names[-state - 1]));
+               e_html_editor_view_insert_smiley (view, (EEmoticon *) emoticon);
+       }
+
+       g_free (node_text);
+}
+
+static void
+html_editor_view_set_links_active (EHTMLEditorView *view,
+                                   gboolean active)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *style;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       if (active) {
+               style = webkit_dom_document_get_element_by_id (
+                               document, "--evolution-editor-style-a");
+               if (style) {
+                       webkit_dom_node_remove_child (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (style)),
+                               WEBKIT_DOM_NODE (style), NULL);
+               }
+       } else {
+               WebKitDOMHTMLHeadElement *head;
+               head = webkit_dom_document_get_head (document);
+
+               style = webkit_dom_document_create_element (document, "STYLE", NULL);
+               webkit_dom_element_set_id (style, "--evolution-editor-style-a");
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (style), "a { cursor: text; }", NULL);
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (head), WEBKIT_DOM_NODE (style), NULL);
+       }
+}
+
+static void
+clipboard_text_received (GtkClipboard *clipboard,
+                         const gchar *text,
+                         EHTMLEditorView *view)
+{
+       EHTMLEditorSelection *selection;
+       gchar *escaped_text;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMElement *blockquote, *element;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range;
+
+       if (!text || !*text)
+               return;
+
+       selection = e_html_editor_view_get_selection (view);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+
+       /* This is a trick to escape any HTML characters (like <, > or &).
+        * <textarea> automatically replaces all these unsafe characters
+        * by &lt;, &gt; etc. */
+       element = webkit_dom_document_create_element (document, "textarea", NULL);
+       webkit_dom_html_element_set_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (element), text, NULL);
+       escaped_text = webkit_dom_html_element_get_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (element));
+
+       element = webkit_dom_document_create_element (document, "pre", NULL);
+
+       webkit_dom_html_element_set_inner_text (
+               WEBKIT_DOM_HTML_ELEMENT (element), escaped_text, NULL);
+
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (element),
+               e_html_editor_selection_get_caret_position_node (document),
+               NULL);
+
+       blockquote = webkit_dom_document_create_element (document, "blockquote", NULL);
+       webkit_dom_element_set_attribute (blockquote, "type", "cite", NULL);
+
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (blockquote), WEBKIT_DOM_NODE (element), NULL);
+
+       if (!e_html_editor_view_get_html_mode (view))
+               e_html_editor_view_quote_plain_text_element (view, element);
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       node = webkit_dom_range_get_end_container (range, NULL);
+
+       webkit_dom_node_append_child (
+               webkit_dom_node_get_parent_node (node),
+               WEBKIT_DOM_NODE (blockquote),
+               NULL);
+
+       e_html_editor_selection_restore_caret_position (selection);
+
+       e_html_editor_view_force_spell_check_for_current_paragraph (view);
+
+       g_free (escaped_text);
+}
+
+static void
+html_editor_view_set_property (GObject *object,
+                               guint property_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CHANGED:
+                       e_html_editor_view_set_changed (
+                               E_HTML_EDITOR_VIEW (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_HTML_MODE:
+                       e_html_editor_view_set_html_mode (
+                               E_HTML_EDITOR_VIEW (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_INLINE_SPELLING:
+                       e_html_editor_view_set_inline_spelling (
+                               E_HTML_EDITOR_VIEW (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_MAGIC_LINKS:
+                       e_html_editor_view_set_magic_links (
+                               E_HTML_EDITOR_VIEW (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_MAGIC_SMILEYS:
+                       e_html_editor_view_set_magic_smileys (
+                               E_HTML_EDITOR_VIEW (object),
+                               g_value_get_boolean (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_view_get_property (GObject *object,
+                               guint property_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CAN_COPY:
+                       g_value_set_boolean (
+                               value, webkit_web_view_can_copy_clipboard (
+                               WEBKIT_WEB_VIEW (object)));
+                       return;
+
+               case PROP_CAN_CUT:
+                       g_value_set_boolean (
+                               value, webkit_web_view_can_cut_clipboard (
+                               WEBKIT_WEB_VIEW (object)));
+                       return;
+
+               case PROP_CAN_PASTE:
+                       g_value_set_boolean (
+                               value, webkit_web_view_can_paste_clipboard (
+                               WEBKIT_WEB_VIEW (object)));
+                       return;
+
+               case PROP_CAN_REDO:
+                       g_value_set_boolean (
+                               value, webkit_web_view_can_redo (
+                               WEBKIT_WEB_VIEW (object)));
+                       return;
+
+               case PROP_CAN_UNDO:
+                       g_value_set_boolean (
+                               value, webkit_web_view_can_undo (
+                               WEBKIT_WEB_VIEW (object)));
+                       return;
+
+               case PROP_CHANGED:
+                       g_value_set_boolean (
+                               value, e_html_editor_view_get_changed (
+                               E_HTML_EDITOR_VIEW (object)));
+                       return;
+
+               case PROP_HTML_MODE:
+                       g_value_set_boolean (
+                               value, e_html_editor_view_get_html_mode (
+                               E_HTML_EDITOR_VIEW (object)));
+                       return;
+
+               case PROP_INLINE_SPELLING:
+                       g_value_set_boolean (
+                               value, e_html_editor_view_get_inline_spelling (
+                               E_HTML_EDITOR_VIEW (object)));
+                       return;
+
+               case PROP_MAGIC_LINKS:
+                       g_value_set_boolean (
+                               value, e_html_editor_view_get_magic_links (
+                               E_HTML_EDITOR_VIEW (object)));
+                       return;
+
+               case PROP_MAGIC_SMILEYS:
+                       g_value_set_boolean (
+                               value, e_html_editor_view_get_magic_smileys (
+                               E_HTML_EDITOR_VIEW (object)));
+                       return;
+
+               case PROP_SPELL_CHECKER:
+                       g_value_set_object (
+                               value, e_html_editor_view_get_spell_checker (
+                               E_HTML_EDITOR_VIEW (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_view_dispose (GObject *object)
+{
+       EHTMLEditorViewPrivate *priv;
+
+       priv = E_HTML_EDITOR_VIEW_GET_PRIVATE (object);
+
+       g_clear_object (&priv->selection);
+
+       if (priv->convertor_web_view != NULL) {
+               g_object_unref (priv->convertor_web_view);
+               priv->convertor_web_view = NULL;
+       }
+
+       if (priv->aliasing_settings != NULL) {
+               g_signal_handlers_disconnect_matched (
+                       priv->aliasing_settings, G_SIGNAL_MATCH_DATA,
+                       0, 0, NULL, NULL, object);
+               g_object_unref (priv->aliasing_settings);
+               priv->aliasing_settings = NULL;
+       }
+
+       if (priv->font_settings != NULL) {
+               g_signal_handlers_disconnect_matched (
+                       priv->font_settings, G_SIGNAL_MATCH_DATA,
+                       0, 0, NULL, NULL, object);
+               g_object_unref (priv->font_settings);
+               priv->font_settings = NULL;
+       }
+
+       g_hash_table_remove_all (priv->inline_images);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_html_editor_view_parent_class)->dispose (object);
+}
+
+static void
+html_editor_view_finalize (GObject *object)
+{
+       EHTMLEditorViewPrivate *priv;
+
+       priv = E_HTML_EDITOR_VIEW_GET_PRIVATE (object);
+
+       g_hash_table_destroy (priv->inline_images);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_html_editor_view_parent_class)->finalize (object);
+}
+
+static void
+html_editor_view_constructed (GObject *object)
+{
+       e_extensible_load_extensions (E_EXTENSIBLE (object));
+
+       /* Chain up to parent's constructed() method. */
+       G_OBJECT_CLASS (e_html_editor_view_parent_class)->constructed (object);
+}
+
+static void
+html_editor_view_save_element_under_mouse_click (GtkWidget *widget)
+{
+       gint x, y;
+       GdkDeviceManager *device_manager;
+       GdkDevice *pointer;
+       EHTMLEditorView *view;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (widget));
+
+       device_manager = gdk_display_get_device_manager (
+               gtk_widget_get_display (GTK_WIDGET (widget)));
+       pointer = gdk_device_manager_get_client_pointer (device_manager);
+       gdk_window_get_device_position (
+               gtk_widget_get_window (GTK_WIDGET (widget)), pointer, &x, &y, NULL);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
+       element = webkit_dom_document_element_from_point (document, x, y);
+
+       view = E_HTML_EDITOR_VIEW (widget);
+       view->priv->element_under_mouse = element;
+}
+
+static gboolean
+html_editor_view_button_press_event (GtkWidget *widget,
+                                     GdkEventButton *event)
+{
+       gboolean event_handled;
+
+       if (event->button == 2) {
+               /* Middle click paste */
+               g_signal_emit (widget, signals[PASTE_PRIMARY_CLIPBOARD], 0);
+               event_handled = TRUE;
+       } else if (event->button == 3) {
+               html_editor_view_save_element_under_mouse_click (widget);
+               g_signal_emit (
+                       widget, signals[POPUP_EVENT],
+                       0, event, &event_handled);
+       } else {
+               event_handled = FALSE;
+       }
+
+       if (event_handled)
+               return TRUE;
+
+       /* Chain up to parent's button_press_event() method. */
+       return GTK_WIDGET_CLASS (e_html_editor_view_parent_class)->
+               button_press_event (widget, event);
+}
+
+static gboolean
+html_editor_view_button_release_event (GtkWidget *widget,
+                                       GdkEventButton *event)
+{
+       WebKitWebView *webview;
+       WebKitHitTestResult *hit_test;
+       WebKitHitTestResultContext context;
+       gchar *uri;
+
+       webview = WEBKIT_WEB_VIEW (widget);
+       hit_test = webkit_web_view_get_hit_test_result (webview, event);
+
+       g_object_get (
+               hit_test,
+               "context", &context,
+               "link-uri", &uri,
+               NULL);
+
+       g_object_unref (hit_test);
+
+       /* Left click on a link */
+       if ((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) &&
+           (event->button == 1)) {
+
+               /* Ctrl + Left Click on link opens it, otherwise ignore the
+                * click completely */
+               if (event->state & GDK_CONTROL_MASK) {
+                       GtkWidget *toplevel;
+                       GdkScreen *screen;
+
+                       toplevel = gtk_widget_get_toplevel (widget);
+                       screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
+                       gtk_show_uri (screen, uri, event->time, NULL);
+                       g_free (uri);
+               }
+
+               return TRUE;
+       }
+
+       g_free (uri);
+
+       /* Chain up to parent's button_release_event() method. */
+       return GTK_WIDGET_CLASS (e_html_editor_view_parent_class)->
+               button_release_event (widget, event);
+}
+
+static gboolean
+insert_new_line_into_citation (EHTMLEditorView *view)
+{
+       EHTMLEditorSelection *selection;
+       gboolean html_mode, ret_val;
+
+       html_mode = e_html_editor_view_get_html_mode (view);
+       selection = e_html_editor_view_get_selection (view);
+
+       ret_val = e_html_editor_view_exec_command (
+               view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL);
+
+       if (ret_val && !html_mode) {
+               WebKitDOMElement *element;
+               WebKitDOMDocument *document;
+               WebKitDOMNode *next_sibling;
+
+               document = webkit_web_view_get_dom_document (
+                       WEBKIT_WEB_VIEW (view));
+
+               element = webkit_dom_document_query_selector (
+                       document, "body>br", NULL);
+
+               next_sibling = webkit_dom_node_get_next_sibling (
+                       WEBKIT_DOM_NODE (element));
+
+               if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (next_sibling)) {
+                       /* Quote content */
+                       next_sibling = WEBKIT_DOM_NODE (
+                               e_html_editor_view_quote_plain_text_element (
+                                       view, WEBKIT_DOM_ELEMENT (next_sibling)));
+                       /* Renew spellcheck */
+                       e_html_editor_view_force_spell_check (view);
+                       /* Insert caret node on right position */
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (element)),
+                               e_html_editor_selection_get_caret_position_node (
+                                       document),
+                               WEBKIT_DOM_NODE (element),
+                               NULL);
+                       /* Restore caret position */
+                       e_html_editor_selection_restore_caret_position (
+                               selection);
+               }
+       }
+
+       return ret_val;
+}
+
+static gboolean
+prevent_from_deleting_last_element_in_body (EHTMLEditorView *view)
+{
+       gboolean ret_val = FALSE;
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNodeList *list;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+
+       list = webkit_dom_node_get_child_nodes (WEBKIT_DOM_NODE (body));
+
+       if (webkit_dom_node_list_get_length (list) <= 1) {
+               gchar *content;
+
+               content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (body));
+
+               if (!*content)
+                       ret_val = TRUE;
+
+               g_free (content);
+
+               if (webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (body), "img", NULL))
+                       ret_val = FALSE;
+       }
+
+       return ret_val;
+}
+
+static gboolean
+html_editor_view_key_press_event (GtkWidget *widget,
+                                  GdkEventKey *event)
+{
+       EHTMLEditorView *view = E_HTML_EDITOR_VIEW (widget);
+
+       if (event->keyval == GDK_KEY_Tab)
+               return e_html_editor_view_exec_command (
+                       view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, "\t");
+
+       if ((event->keyval == GDK_KEY_Control_L) ||
+           (event->keyval == GDK_KEY_Control_R)) {
+
+               html_editor_view_set_links_active (view, TRUE);
+       }
+
+       if ((event->keyval == GDK_KEY_Return) ||
+           (event->keyval == GDK_KEY_KP_Enter)) {
+               EHTMLEditorSelection *selection;
+
+               selection = e_html_editor_view_get_selection (view);
+               /* When user presses ENTER in a citation block, WebKit does
+                * not break the citation automatically, so we need to use
+                * the special command to do it. */
+               if (e_html_editor_selection_is_citation (selection))
+                       return insert_new_line_into_citation (view);
+       }
+
+       /* BackSpace in indented block decrease indent level by one */
+       if (event->keyval == GDK_KEY_BackSpace) {
+               EHTMLEditorSelection *selection;
+
+               selection = e_html_editor_view_get_selection (view);
+               if (e_html_editor_selection_is_indented (selection)) {
+                       WebKitDOMElement *caret;
+
+                       caret = e_html_editor_selection_save_caret_position (selection);
+
+                       if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (caret))) {
+                               e_html_editor_selection_clear_caret_position_marker (selection);
+                               e_html_editor_selection_unindent (selection);
+                               return TRUE;
+                       } else
+                               e_html_editor_selection_clear_caret_position_marker (selection);
+               }
+
+               if (prevent_from_deleting_last_element_in_body (view))
+                       return TRUE;
+       }
+
+       /* Chain up to parent's key_press_event() method. */
+       return GTK_WIDGET_CLASS (e_html_editor_view_parent_class)->
+               key_press_event (widget, event);
+}
+
+static void
+mark_node_as_paragraph_after_ending_list (EHTMLEditorSelection *selection,
+                                          WebKitDOMDocument *document)
+{
+       gint ii, length;
+       WebKitDOMNodeList *list;
+
+       /* When pressing Enter on empty line in the list WebKit will end that
+        * list and inserts <div><br></div> so mark it for wrapping */
+       list = webkit_dom_document_query_selector_all (
+               document, "body > div:not(.-x-evo-paragraph) > br", NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_get_parent_node (
+                       webkit_dom_node_list_item (list, ii));
+
+               e_html_editor_selection_set_paragraph_style (
+                       selection, WEBKIT_DOM_ELEMENT (node), -1, 0, "");
+       }
+}
+
+static gboolean
+surround_text_with_paragraph_if_needed (EHTMLEditorSelection *selection,
+                                        WebKitDOMDocument *document,
+                                        WebKitDOMNode *node)
+{
+       WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (node);
+       WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (node);
+       WebKitDOMElement *element;
+
+       /* All text in composer has to be written in div elements, so if
+        * we are writing something straight to the body, surround it with
+        * paragraph */
+       if (WEBKIT_DOM_IS_TEXT (node) &&
+           WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node))) {
+               element = e_html_editor_selection_put_node_into_paragraph (
+                       selection,
+                       document,
+                       node,
+                       e_html_editor_selection_get_caret_position_node (document));
+
+               if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling)) {
+                       webkit_dom_node_remove_child (
+                               webkit_dom_node_get_parent_node (next_sibling),
+                               next_sibling,
+                               NULL);
+               }
+
+               /* Tab character */
+               if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "Apple-tab-span")) {
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (element),
+                               prev_sibling,
+                               webkit_dom_node_get_first_child (
+                                       WEBKIT_DOM_NODE (element)),
+                               NULL);
+               }
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean
+html_editor_view_key_release_event (GtkWidget *widget,
+                                    GdkEventKey *event)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMRange *range;
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+
+       view = E_HTML_EDITOR_VIEW (widget);
+       range = html_editor_view_get_dom_range (view);
+       selection = e_html_editor_view_get_selection (view);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
+
+       if (view->priv->magic_smileys &&
+           view->priv->html_mode) {
+               html_editor_view_check_magic_smileys (view, range);
+       }
+
+       if ((event->keyval == GDK_KEY_Return) ||
+           (event->keyval == GDK_KEY_Linefeed) ||
+           (event->keyval == GDK_KEY_KP_Enter) ||
+           (event->keyval == GDK_KEY_space)) {
+
+               html_editor_view_check_magic_links (view, range, FALSE, event);
+
+               mark_node_as_paragraph_after_ending_list (selection, document);
+       } else {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_range_get_end_container (range, NULL);
+
+               if (surround_text_with_paragraph_if_needed (selection, document, node)) {
+                       e_html_editor_selection_restore_caret_position (selection);
+                       node = webkit_dom_range_get_end_container (range, NULL);
+                       range = html_editor_view_get_dom_range (view);
+               }
+
+               if (WEBKIT_DOM_IS_TEXT (node)) {
+                       gchar *text;
+
+                       text = webkit_dom_node_get_text_content (node);
+
+                       if (g_strcmp0 (text, "") != 0 && !g_unichar_isspace (g_utf8_get_char (text))) {
+                               WebKitDOMNode *prev_sibling;
+
+                               prev_sibling = webkit_dom_node_get_previous_sibling (node);
+
+                               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling))
+                                       html_editor_view_check_magic_links (view, range, FALSE, event);
+                       }
+                       g_free (text);
+               }
+       }
+
+       if ((event->keyval == GDK_KEY_Control_L) ||
+           (event->keyval == GDK_KEY_Control_R)) {
+
+               html_editor_view_set_links_active (view, FALSE);
+       }
+
+       /* Chain up to parent's key_release_event() method. */
+       return GTK_WIDGET_CLASS (e_html_editor_view_parent_class)->
+               key_release_event (widget, event);
+}
+
+static void
+html_editor_view_paste_clipboard_quoted (EHTMLEditorView *view)
+{
+       GtkClipboard *clipboard;
+
+       clipboard = gtk_clipboard_get_for_display (
+               gdk_display_get_default (),
+               GDK_SELECTION_CLIPBOARD);
+
+       gtk_clipboard_request_text (
+               clipboard,
+               (GtkClipboardTextReceivedFunc) clipboard_text_received,
+               view);
+}
+
+static gboolean
+html_editor_view_image_exists_in_cache (const gchar *image_uri)
+{
+       gchar *filename;
+       gchar *hash;
+       gboolean exists = FALSE;
+
+       g_return_val_if_fail (emd_global_http_cache != NULL, FALSE);
+
+       hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, image_uri, -1);
+       filename = camel_data_cache_get_filename (
+               emd_global_http_cache, "http", hash);
+
+       if (filename != NULL) {
+               exists = g_file_test (filename, G_FILE_TEST_EXISTS);
+               g_free (filename);
+       }
+
+       g_free (hash);
+
+       return exists;
+}
+
+static gchar *
+html_editor_view_redirect_uri (EHTMLEditorView *view,
+                               const gchar *uri)
+{
+       EImageLoadingPolicy image_policy;
+       GSettings *settings;
+       gboolean uri_is_http;
+
+       uri_is_http =
+               g_str_has_prefix (uri, "http:") ||
+               g_str_has_prefix (uri, "https:") ||
+               g_str_has_prefix (uri, "evo-http:") ||
+               g_str_has_prefix (uri, "evo-https:");
+
+       /* Redirect http(s) request to evo-http(s) protocol.
+        * See EMailRequest for further details about this. */
+       if (uri_is_http) {
+               gchar *new_uri;
+               SoupURI *soup_uri;
+               gboolean image_exists;
+
+               /* Check Evolution's cache */
+               image_exists = html_editor_view_image_exists_in_cache (uri);
+
+               settings = g_settings_new ("org.gnome.evolution.mail");
+               image_policy = g_settings_get_enum (settings, "image-loading-policy");
+               g_object_unref (settings);
+               /* If the URI is not cached and we are not allowed to load it
+                * then redirect to invalid URI, so that webkit would display
+                * a native placeholder for it. */
+               if (!image_exists && (image_policy == E_IMAGE_LOADING_POLICY_NEVER)) {
+                       return g_strdup ("about:blank");
+               }
+
+               new_uri = g_strconcat ("evo-", uri, NULL);
+               soup_uri = soup_uri_new (new_uri);
+               g_free (new_uri);
+
+               new_uri = soup_uri_to_string (soup_uri, FALSE);
+
+               soup_uri_free (soup_uri);
+
+               return new_uri;
+       }
+
+       return g_strdup (uri);
+}
+
+static void
+html_editor_view_resource_requested (WebKitWebView *web_view,
+                                     WebKitWebFrame *frame,
+                                     WebKitWebResource *resource,
+                                     WebKitNetworkRequest *request,
+                                     WebKitNetworkResponse *response,
+                                     gpointer user_data)
+{
+       const gchar *original_uri;
+
+       original_uri = webkit_network_request_get_uri (request);
+
+       if (original_uri != NULL) {
+               gchar *redirected_uri;
+
+               redirected_uri = html_editor_view_redirect_uri (
+                       E_HTML_EDITOR_VIEW (web_view), original_uri);
+
+               webkit_network_request_set_uri (request, redirected_uri);
+
+               g_free (redirected_uri);
+       }
+}
+
+static void
+e_html_editor_view_class_init (EHTMLEditorViewClass *class)
+{
+       GObjectClass *object_class;
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorViewPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->get_property = html_editor_view_get_property;
+       object_class->set_property = html_editor_view_set_property;
+       object_class->dispose = html_editor_view_dispose;
+       object_class->finalize = html_editor_view_finalize;
+       object_class->constructed = html_editor_view_constructed;
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->button_press_event = html_editor_view_button_press_event;
+       widget_class->button_release_event = html_editor_view_button_release_event;
+       widget_class->key_press_event = html_editor_view_key_press_event;
+       widget_class->key_release_event = html_editor_view_key_release_event;
+
+       class->paste_clipboard_quoted = html_editor_view_paste_clipboard_quoted;
+
+       /**
+        * EHTMLEditorView:can-copy
+        *
+        * Determines whether it's possible to copy to clipboard. The action
+        * is usually disabled when there is no selection to copy.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_CAN_COPY,
+               g_param_spec_boolean (
+                       "can-copy",
+                       "Can Copy",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorView:can-cut
+        *
+        * Determines whether it's possible to cut to clipboard. The action
+        * is usually disabled when there is no selection to cut.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_CAN_CUT,
+               g_param_spec_boolean (
+                       "can-cut",
+                       "Can Cut",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorView:can-paste
+        *
+        * Determines whether it's possible to paste from clipboard. The action
+        * is usually disabled when there is no valid content in clipboard to
+        * paste.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_CAN_PASTE,
+               g_param_spec_boolean (
+                       "can-paste",
+                       "Can Paste",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorView:can-redo
+        *
+        * Determines whether it's possible to redo previous action. The action
+        * is usually disabled when there is no action to redo.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_CAN_REDO,
+               g_param_spec_boolean (
+                       "can-redo",
+                       "Can Redo",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorView:can-undo
+        *
+        * Determines whether it's possible to undo last action. The action
+        * is usually disabled when there is no previous action to undo.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_CAN_UNDO,
+               g_param_spec_boolean (
+                       "can-undo",
+                       "Can Undo",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorView:changed
+        *
+        * Determines whether document has been modified
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_CHANGED,
+               g_param_spec_boolean (
+                       "changed",
+                       _("Changed property"),
+                       _("Whether editor changed"),
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorView:html-mode
+        *
+        * Determines whether HTML or plain text mode is enabled.
+        **/
+       g_object_class_install_property (
+               object_class,
+               PROP_HTML_MODE,
+               g_param_spec_boolean (
+                       "html-mode",
+                       "HTML Mode",
+                       "Edit HTML or plain text",
+                       TRUE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorView::inline-spelling
+        *
+        * Determines whether automatic spellchecking is enabled.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_INLINE_SPELLING,
+               g_param_spec_boolean (
+                       "inline-spelling",
+                       "Inline Spelling",
+                       "Check your spelling as you type",
+                       TRUE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorView:magic-links
+        *
+        * Determines whether automatic conversion of text links into
+        * HTML links is enabled.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_MAGIC_LINKS,
+               g_param_spec_boolean (
+                       "magic-links",
+                       "Magic Links",
+                       "Make URIs clickable as you type",
+                       TRUE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorView:magic-smileys
+        *
+        * Determines whether automatic conversion of text smileys into
+        * images is enabled.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_MAGIC_SMILEYS,
+               g_param_spec_boolean (
+                       "magic-smileys",
+                       "Magic Smileys",
+                       "Convert emoticons to images as you type",
+                       TRUE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorView:spell-checker:
+        *
+        * The #ESpellChecker used for spell checking.
+        **/
+       g_object_class_install_property (
+               object_class,
+               PROP_SPELL_CHECKER,
+               g_param_spec_object (
+                       "spell-checker",
+                       "Spell Checker",
+                       "The spell checker",
+                       E_TYPE_SPELL_CHECKER,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorView:popup-event
+        *
+        * Emitted whenever a context menu is requested.
+        */
+       signals[POPUP_EVENT] = g_signal_new (
+               "popup-event",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EHTMLEditorViewClass, popup_event),
+               g_signal_accumulator_true_handled, NULL,
+               e_marshal_BOOLEAN__BOXED,
+               G_TYPE_BOOLEAN, 1,
+               GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+       /**
+        * EHTMLEditorView:paste-primary-clipboad
+        *
+        * Emitted when user presses middle button on EHTMLEditorView
+        */
+       signals[PASTE_PRIMARY_CLIPBOARD] = g_signal_new (
+               "paste-primary-clipboard",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EHTMLEditorViewClass, paste_primary_clipboard),
+               NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+}
+
+static GString *
+replace_string_spaces_with_nbsp_in_prefix (const gchar *text)
+{
+       GString *str;
+       gint counter = 0;
+
+       g_return_val_if_fail (text != NULL, NULL);
+
+       str = g_string_new ("");
+
+       while (g_str_has_prefix (text + counter, " ")) {
+               g_string_append (str, "&nbsp;");
+
+               counter++;
+       }
+
+       g_string_append (str, text + counter);
+
+       return str;
+}
+
+/* This parses the HTML code (that contains just text, &nbsp; and BR elements)
+ * into paragraphs.
+ * HTML code in that format we can get by taking innerText from some element,
+ * setting it to another one and finally getting innerHTML from it */
+static void
+parse_html_into_paragraphs (EHTMLEditorView *view,
+                            WebKitDOMDocument *document,
+                            WebKitDOMElement *blockquote,
+                            const gchar *html,
+                            gboolean use_pre)
+{
+       const gchar *prev_br, *next_br;
+       gchar *inner_html;
+       gint citation_level = 0;
+       GString *start, *end;
+       gboolean ignore_next_br = FALSE;
+
+       webkit_dom_html_element_set_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (blockquote), "", NULL);
+
+       prev_br = html;
+       next_br = strstr (prev_br, "<br>");
+
+       while (next_br) {
+               gboolean local_ignore_next_br = ignore_next_br;
+               const gchar *citation = NULL, *citation_end = NULL;
+               const gchar *rest = NULL, *with_br = NULL;
+               gchar *to_insert = NULL;
+               WebKitDOMElement *paragraph;
+
+               to_insert = g_utf8_substring (
+                       prev_br, 0, g_utf8_pointer_to_offset (prev_br, next_br));
+
+               with_br = strstr (to_insert, "<br>");
+
+               ignore_next_br = FALSE;
+
+               citation = strstr (to_insert, "##CITATION_");
+               if (citation) {
+                       if (strstr (to_insert, "##CITATION_START##"))
+                               citation_level++;
+                       else
+                               citation_level--;
+
+                       citation_end = strstr (citation + 2, "##");
+                       if (citation_end)
+                               rest = citation_end + 2;
+               } else {
+                       rest = with_br ?
+                               to_insert + 4 + (with_br - to_insert) : to_insert;
+               }
+
+               if (use_pre) {
+                       paragraph = webkit_dom_document_create_element (
+                               document, "pre", NULL);
+               } else {
+                       paragraph = e_html_editor_selection_get_paragraph_element (
+                               e_html_editor_view_get_selection (view),
+                               document, -1, citation_level);
+               }
+
+               if (with_br && !*rest && !citation &&!local_ignore_next_br) {
+                       WebKitDOMNode *paragraph_clone;
+
+                       paragraph_clone = webkit_dom_node_clone_node (
+                               WEBKIT_DOM_NODE (paragraph), TRUE);
+
+                       webkit_dom_html_element_set_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (paragraph_clone),
+                               "&nbsp;",
+                               NULL);
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (blockquote),
+                               paragraph_clone,
+                               NULL);
+               }
+
+               if (citation) {
+                       WebKitDOMText *text;
+                       gchar *citation_mark;
+
+                       citation_mark = g_utf8_substring (
+                               citation, 0,
+                               g_utf8_pointer_to_offset (
+                                       citation, citation_end + 2));
+
+                       text = webkit_dom_document_create_text_node (
+                               document, citation_mark);
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (blockquote),
+                               WEBKIT_DOM_NODE (text),
+                               NULL);
+
+                       g_free (citation_mark);
+               }
+
+               if (rest && *rest){
+                       GString *space_to_nbsp;
+
+                       space_to_nbsp = replace_string_spaces_with_nbsp_in_prefix (rest);
+                       webkit_dom_html_element_set_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (paragraph),
+                               space_to_nbsp->str,
+                               NULL);
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (blockquote),
+                               WEBKIT_DOM_NODE (paragraph),
+                               NULL);
+                       g_string_free (space_to_nbsp, TRUE);
+               }
+
+               if (citation_end)
+                       ignore_next_br = TRUE;
+
+               prev_br = next_br;
+               next_br = strstr (prev_br + 4, "<br>");
+               g_free (to_insert);
+       }
+
+       if (g_utf8_strlen (prev_br, -1) > 0) {
+               WebKitDOMElement *paragraph;
+
+               if (use_pre) {
+                       paragraph = webkit_dom_document_create_element (
+                               document, "pre", NULL);
+               } else {
+                       paragraph = e_html_editor_selection_get_paragraph_element (
+                               e_html_editor_view_get_selection (view),
+                               document, -1, citation_level);
+               }
+
+               webkit_dom_html_element_set_inner_html (
+                       WEBKIT_DOM_HTML_ELEMENT (paragraph),
+                       g_str_has_prefix (prev_br, "<br>") ? prev_br + 4 : prev_br,
+                       NULL);
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (blockquote),
+                       WEBKIT_DOM_NODE (paragraph),
+                       NULL);
+       }
+
+       /* Replace text markers with actual HTML blockquotes */
+       inner_html = webkit_dom_html_element_get_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (blockquote));
+       start = e_str_replace_string (
+               inner_html, "##CITATION_START##","<blockquote type=\"cite\">");
+       end = e_str_replace_string (
+               start->str, "##CITATION_END##", "</blockquote>");
+       webkit_dom_html_element_set_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (blockquote), end->str, NULL);
+
+       g_free (inner_html);
+       g_string_free (start, TRUE);
+       g_string_free (end, TRUE);
+}
+
+static void
+mark_citation (WebKitDOMElement *citation)
+{
+       gchar *inner_html, *surrounded;
+
+       inner_html = webkit_dom_html_element_get_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (citation));
+
+       surrounded = g_strconcat (
+               "<span>##CITATION_START##</span>", inner_html,
+               "<span>##CITATION_END##</span>", NULL);
+
+       webkit_dom_html_element_set_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (citation), surrounded, NULL);
+
+       element_add_class (citation, "marked");
+
+       g_free (inner_html);
+       g_free (surrounded);
+}
+
+static gint
+create_text_markers_for_citations_in_document (WebKitDOMDocument *document)
+{
+       gint count = 0;
+       WebKitDOMElement *citation;
+
+       citation = webkit_dom_document_query_selector (
+               document, "blockquote[type=cite]:not(.marked)", NULL);
+
+       while (citation) {
+               mark_citation (citation);
+               count ++;
+
+               citation = webkit_dom_document_query_selector (
+                       document, "blockquote[type=cite]:not(.marked)", NULL);
+       }
+
+       return count;
+}
+
+static gint
+create_text_markers_for_citations_in_element (WebKitDOMElement *element)
+{
+       gint count = 0;
+       WebKitDOMElement *citation;
+
+       citation = webkit_dom_element_query_selector (
+               element, "blockquote[type=cite]:not(.marked)", NULL);
+
+       while (citation) {
+               mark_citation (citation);
+               count ++;
+
+               citation = webkit_dom_element_query_selector (
+                       element, "blockquote[type=cite]:not(.marked)", NULL);
+       }
+
+       return count;
+}
+
+static void
+html_editor_view_process_document_from_convertor (EHTMLEditorView *view,
+                                                  WebKitDOMDocument *document_convertor)
+{
+       EHTMLEditorSelection *selection = e_html_editor_view_get_selection (view);
+       gboolean start_bottom;
+       gchar *inner_text, *inner_html;
+       gint ii;
+       GSettings *settings;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *paragraph, *new_blockquote, *top_signature;
+       WebKitDOMElement *cite_body, *signature;
+       WebKitDOMHTMLElement *body, *body_convertor;
+       WebKitDOMNodeList *list;
+
+       settings = g_settings_new ("org.gnome.evolution.mail");
+       start_bottom = g_settings_get_boolean (settings, "composer-reply-start-bottom");
+       g_object_unref (settings);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+       body_convertor = webkit_dom_document_get_body (document_convertor);
+
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-converted", "", NULL);
+
+       paragraph = webkit_dom_document_get_element_by_id (document, "-x-evo-input-start");
+       if (!paragraph) {
+               paragraph = e_html_editor_selection_get_paragraph_element (
+                       selection, document, -1, 0);
+               webkit_dom_element_set_id (paragraph, "-x-evo-input-start");
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (paragraph), UNICODE_ZERO_WIDTH_SPACE, NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)),
+                       WEBKIT_DOM_NODE (paragraph),
+                       NULL);
+       }
+
+       list = webkit_dom_document_query_selector_all (
+               document_convertor, "span.-x-evo-to-body", NULL);
+       for (ii = webkit_dom_node_list_get_length (list) - 1; ii >= 0; ii--) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (list, ii);
+               while (webkit_dom_node_has_child_nodes (node)) {
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (body),
+                               webkit_dom_node_clone_node (
+                                       webkit_dom_node_get_first_child (node), TRUE),
+                               webkit_dom_node_get_next_sibling (
+                                       WEBKIT_DOM_NODE (paragraph)),
+                               NULL);
+
+                       webkit_dom_node_remove_child (
+                               node, webkit_dom_node_get_first_child (node), NULL);
+               }
+
+               webkit_dom_node_remove_child (
+                       WEBKIT_DOM_NODE (body_convertor),
+                       WEBKIT_DOM_NODE (node),
+                       NULL);
+       }
+
+       repair_gmail_blockquotes (document_convertor);
+
+       create_text_markers_for_citations_in_document (document_convertor);
+
+       /* Get innertText from convertor */
+       inner_text = webkit_dom_html_element_get_inner_text (body_convertor);
+
+       cite_body = webkit_dom_document_query_selector (
+               document_convertor, "span.-x-evo-cite-body", NULL);
+
+       top_signature = webkit_dom_document_query_selector (
+               document, ".-x-evo-top-signature", NULL);
+       signature = webkit_dom_document_query_selector (
+               document, "span.-x-evo-signature", NULL);
+
+       if (cite_body) {
+               if (!(top_signature && start_bottom))
+                       e_html_editor_selection_save_caret_position (selection);
+       } else {
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (paragraph),
+                       WEBKIT_DOM_NODE (
+                               e_html_editor_selection_get_caret_position_node (
+                                       document)),
+                       NULL);
+       }
+
+       new_blockquote = webkit_dom_document_create_element (
+               document, "blockquote", NULL);
+       webkit_dom_element_set_attribute (
+               new_blockquote, "type", "cite", NULL);
+
+       webkit_dom_html_element_set_inner_text (
+               WEBKIT_DOM_HTML_ELEMENT (new_blockquote), inner_text, NULL);
+       inner_html = webkit_dom_html_element_get_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (new_blockquote));
+
+       if (cite_body) {
+               webkit_dom_element_set_attribute (
+                       new_blockquote, "id", "-x-evo-main-cite", NULL);
+
+               parse_html_into_paragraphs (
+                       view, document, new_blockquote, inner_html, FALSE);
+
+               if (start_bottom) {
+                       if (signature) {
+                               WebKitDOMNode *parent =
+                                       webkit_dom_node_get_parent_node (
+                                               WEBKIT_DOM_NODE (signature));
+                               if (top_signature) {
+                                       webkit_dom_node_append_child (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (new_blockquote),
+                                               NULL);
+                                       webkit_dom_node_append_child (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (paragraph),
+                                               NULL);
+                                       webkit_dom_node_append_child (
+                                               WEBKIT_DOM_NODE (paragraph),
+                                               e_html_editor_selection_get_caret_position_node (
+                                                       document),
+                                               NULL);
+                               } else {
+                                       webkit_dom_node_insert_before (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (new_blockquote),
+                                               WEBKIT_DOM_NODE (parent),
+                                               NULL);
+                                       webkit_dom_node_insert_before (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (paragraph),
+                                               webkit_dom_node_get_next_sibling (
+                                                       WEBKIT_DOM_NODE (new_blockquote)),
+                                               NULL);
+                               }
+                       } else {
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (new_blockquote),
+                                       NULL);
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (paragraph),
+                                       webkit_dom_node_get_next_sibling (
+                                               WEBKIT_DOM_NODE (new_blockquote)),
+                                       NULL);
+                       }
+               } else {
+                       if (signature) {
+                               WebKitDOMNode *parent =
+                                       webkit_dom_node_get_parent_node (
+                                               WEBKIT_DOM_NODE (signature));
+
+                               if (top_signature) {
+                                       WebKitDOMElement *br;
+
+                                       br = webkit_dom_document_create_element (
+                                               document, "BR", NULL);
+
+                                       webkit_dom_node_append_child (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (new_blockquote),
+                                               NULL);
+                                       /* Insert NL after signature */
+                                       webkit_dom_node_insert_before (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (br),
+                                               webkit_dom_node_get_next_sibling (
+                                                       WEBKIT_DOM_NODE (paragraph)),
+                                               NULL);
+                                       webkit_dom_node_insert_before (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (parent),
+                                               WEBKIT_DOM_NODE (br),
+                                               NULL);
+                               } else
+                                       webkit_dom_node_insert_before (
+                                               WEBKIT_DOM_NODE (body),
+                                               WEBKIT_DOM_NODE (new_blockquote),
+                                               WEBKIT_DOM_NODE (parent),
+                                               NULL);
+                       } else {
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (new_blockquote),
+                                       NULL);
+                       }
+               }
+       } else {
+               WebKitDOMNode *signature_clone, *first_child;
+
+               if (signature) {
+                       signature_clone = webkit_dom_node_clone_node (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (signature)),
+                               TRUE);
+               }
+
+               parse_html_into_paragraphs (
+                       view, document, WEBKIT_DOM_ELEMENT (body),
+                       inner_html, FALSE);
+
+               if (signature) {
+                       if (!top_signature) {
+                               signature_clone = webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (body),
+                                       signature_clone,
+                                       NULL);
+                       } else {
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (body),
+                                       signature_clone,
+                                       webkit_dom_node_get_first_child (
+                                               WEBKIT_DOM_NODE (body)),
+                                       NULL);
+                       }
+               }
+
+               first_child = webkit_dom_node_get_first_child (
+                       WEBKIT_DOM_NODE (body));
+
+               webkit_dom_node_insert_before (
+                       first_child,
+                       e_html_editor_selection_get_caret_position_node (
+                               document),
+                       webkit_dom_node_get_first_child (first_child),
+                       NULL);
+       }
+
+       if (!e_html_editor_view_get_html_mode (view))
+               e_html_editor_selection_wrap_paragraphs_in_document (
+                       selection, document);
+       if (webkit_dom_document_query_selector (document, "blockquote[type=cite]", NULL))
+               body = WEBKIT_DOM_HTML_ELEMENT (
+                       e_html_editor_view_quote_plain_text (view));
+
+       e_html_editor_selection_restore_caret_position (selection);
+       e_html_editor_view_force_spell_check (view);
+
+       /* Register on input event that is called when the content (body) is modified */
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (body),
+               "input",
+               G_CALLBACK (body_input_event_cb),
+               FALSE,
+               view);
+
+       g_free (inner_html);
+       g_free (inner_text);
+}
+
+static void
+html_editor_view_insert_converted_html_into_selection (EHTMLEditorView *view,
+                                                       WebKitDOMDocument *document_convertor)
+{
+       gchar *inner_text, *inner_html;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+       WebKitDOMHTMLElement *convertor_body;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       convertor_body = webkit_dom_document_get_body (document_convertor);
+
+       inner_text = webkit_dom_html_element_get_inner_text (convertor_body);
+       element = webkit_dom_document_create_element (document, "div", NULL);
+       webkit_dom_html_element_set_inner_text (
+               WEBKIT_DOM_HTML_ELEMENT (element), inner_text, NULL);
+       inner_html = webkit_dom_html_element_get_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (element));
+
+       parse_html_into_paragraphs (
+               view, document, element, inner_html, FALSE);
+
+       g_free (inner_html);
+
+       inner_html = webkit_dom_html_element_get_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (element));
+
+       e_html_editor_view_exec_command (
+               view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, inner_html);
+
+       e_html_editor_view_force_spell_check (view);
+
+       g_free (inner_html);
+       g_free (inner_text);
+}
+
+static void
+html_plain_text_convertor_load_status_changed (WebKitWebView *web_view,
+                                               GParamSpec *pspec,
+                                               EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document_convertor;
+
+       if (webkit_web_view_get_load_status (web_view) != WEBKIT_LOAD_FINISHED)
+               return;
+
+       document_convertor = webkit_web_view_get_dom_document (web_view);
+
+       if (view->priv->convertor_insert)
+               html_editor_view_insert_converted_html_into_selection (
+                       view, document_convertor);
+       else
+               html_editor_view_process_document_from_convertor (
+                       view, document_convertor);
+}
+
+static void
+e_html_editor_view_init (EHTMLEditorView *view)
+{
+       WebKitWebSettings *settings;
+       GSettings *g_settings;
+       GSettingsSchema *settings_schema;
+       ESpellChecker *checker;
+       gchar **languages;
+       gchar *comma_separated;
+       const gchar *user_cache_dir;
+
+       view->priv = E_HTML_EDITOR_VIEW_GET_PRIVATE (view);
+
+       webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), TRUE);
+       settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
+
+       g_object_set (
+               G_OBJECT (settings),
+               "enable-developer-extras", TRUE,
+               "enable-dom-paste", TRUE,
+               "enable-file-access-from-file-uris", TRUE,
+               "enable-plugins", FALSE,
+               "enable-scripts", FALSE,
+               "enable-spell-checking", TRUE,
+               "respect-image-orientation", TRUE,
+               NULL);
+
+       webkit_web_view_set_settings (WEBKIT_WEB_VIEW (view), settings);
+
+       /* Override the spell-checker, use our own */
+       checker = e_spell_checker_new ();
+       webkit_set_text_checker (G_OBJECT (checker));
+       g_object_unref (checker);
+
+       /* Don't use CSS when possible to preserve compatibility with older
+        * versions of Evolution or other MUAs */
+       e_html_editor_view_exec_command (
+               view, E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS, "false");
+
+       g_signal_connect (
+               view, "user-changed-contents",
+               G_CALLBACK (html_editor_view_user_changed_contents_cb), NULL);
+       g_signal_connect (
+               view, "selection-changed",
+               G_CALLBACK (html_editor_view_selection_changed_cb), NULL);
+       g_signal_connect (
+               view, "should-show-delete-interface-for-element",
+               G_CALLBACK (html_editor_view_should_show_delete_interface_for_element), NULL);
+       g_signal_connect (
+               view, "resource-request-starting",
+               G_CALLBACK (html_editor_view_resource_requested), NULL);
+       g_signal_connect (
+               view, "notify::load-status",
+               G_CALLBACK (html_editor_view_load_status_changed), NULL);
+
+       view->priv->selection = g_object_new (
+               E_TYPE_HTML_EDITOR_SELECTION,
+               "html-editor-view", view,
+               NULL);
+
+       g_settings = g_settings_new ("org.gnome.desktop.interface");
+       g_signal_connect_swapped (
+               g_settings, "changed::font-name",
+               G_CALLBACK (e_html_editor_view_update_fonts), view);
+       g_signal_connect_swapped (
+               g_settings, "changed::monospace-font-name",
+               G_CALLBACK (e_html_editor_view_update_fonts), view);
+       view->priv->font_settings = g_settings;
+
+       /* This schema is optional.  Use if available. */
+       settings_schema = g_settings_schema_source_lookup (
+               g_settings_schema_source_get_default (),
+               "org.gnome.settings-daemon.plugins.xsettings", FALSE);
+       if (settings_schema != NULL) {
+               g_settings = g_settings_new ("org.gnome.settings-daemon.plugins.xsettings");
+               g_signal_connect_swapped (
+                       settings, "changed::antialiasing",
+                       G_CALLBACK (e_html_editor_view_update_fonts), view);
+               view->priv->aliasing_settings = g_settings;
+       }
+
+       view->priv->inline_images = g_hash_table_new_full (
+               g_str_hash, g_str_equal,
+               (GDestroyNotify) g_free,
+               (GDestroyNotify) g_free);
+
+       e_html_editor_view_update_fonts (view);
+
+       /* Give spell check languages to WebKit */
+       languages = e_spell_checker_list_active_languages (checker, NULL);
+       comma_separated = g_strjoinv (",", languages);
+       g_strfreev (languages);
+
+       g_object_set (
+               G_OBJECT (settings),
+               "spell-checking-languages", comma_separated,
+               NULL);
+
+       g_free (comma_separated);
+
+       view->priv->convertor_insert = FALSE;
+
+       view->priv->convertor_web_view =
+               g_object_ref_sink (WEBKIT_WEB_VIEW (webkit_web_view_new ()));
+       settings = webkit_web_view_get_settings (view->priv->convertor_web_view);
+
+       g_object_set (
+               G_OBJECT (settings),
+               "enable-scripts", FALSE,
+               "enable-plugins", FALSE,
+               NULL);
+
+       g_signal_connect (
+               view->priv->convertor_web_view, "notify::load-status",
+               G_CALLBACK (html_plain_text_convertor_load_status_changed), view);
+
+       /* Make WebKit think we are displaying a local file, so that it
+        * does not block loading resources from file:// protocol */
+       webkit_web_view_load_string (
+               WEBKIT_WEB_VIEW (view), "", "text/html", "UTF-8", "file://");
+
+       html_editor_view_set_links_active (view, FALSE);
+
+       if (emd_global_http_cache == NULL) {
+               user_cache_dir = e_get_user_cache_dir ();
+               emd_global_http_cache = camel_data_cache_new (user_cache_dir, NULL);
+
+               /* cache expiry - 2 hour access, 1 day max */
+               camel_data_cache_set_expire_age (
+                       emd_global_http_cache, 24 * 60 * 60);
+               camel_data_cache_set_expire_access (
+                       emd_global_http_cache, 2 * 60 * 60);
+       }
+}
+
+/**
+ * e_html_editor_view_new:
+ *
+ * Returns a new instance of the editor.
+ *
+ * Returns: A newly created #EHTMLEditorView. [transfer-full]
+ */
+EHTMLEditorView *
+e_html_editor_view_new (void)
+{
+       return g_object_new (E_TYPE_HTML_EDITOR_VIEW, NULL);
+}
+
+/**
+ * e_html_editor_view_get_selection:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns an #EHTMLEditorSelection object which represents current selection or
+ * cursor position within the editor document. The #EHTMLEditorSelection allows
+ * programmer to manipulate with formatting, selection, styles etc.
+ *
+ * Returns: An always valid #EHTMLEditorSelection object. The object is owned by
+ * the @view and should never be free'd.
+ */
+EHTMLEditorSelection *
+e_html_editor_view_get_selection (EHTMLEditorView *view)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL);
+
+       return view->priv->selection;
+}
+
+/**
+ * e_html_editor_view_exec_command:
+ * @view: an #EHTMLEditorView
+ * @command: an #EHTMLEditorViewCommand to execute
+ * @value: value of the command (or @NULL if the command does not require value)
+ *
+ * The function will fail when @value is @NULL or empty but the current @command
+ * requires a value to be passed. The @value is ignored when the @command does
+ * not expect any value.
+ *
+ * Returns: @TRUE when the command was succesfully executed, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_exec_command (EHTMLEditorView *view,
+                                EHTMLEditorViewCommand command,
+                                const gchar *value)
+{
+       WebKitDOMDocument *document;
+       const gchar *cmd_str = 0;
+       gboolean has_value;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+#define CHECK_COMMAND(cmd,str,val) case cmd:\
+       if (val) {\
+               g_return_val_if_fail (value && *value, FALSE);\
+       }\
+       has_value = val; \
+       cmd_str = str;\
+       break;
+
+       switch (command) {
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR, "BackColor", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_BOLD, "Bold", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_COPY, "Copy", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK, "CreateLink", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_CUT, "Cut", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR, 
"DefaultParagraphSeparator", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_DELETE, "Delete", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FIND_STRING, "FindString", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME, "FontName", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE, "FontSize", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE_DELTA, "FontSizeDelta", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR, "ForeColor", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FORMAT_BLOCK, "FormatBlock", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE, "ForwardDelete", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_HILITE_COLOR, "HiliteColor", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INDENT, "Indent", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_HORIZONTAL_RULE, "InsertHorizontalRule", 
FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, "InsertHTML", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_IMAGE, "InsertImage", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_LINE_BREAK, "InsertLineBreak", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, 
"InsertNewlineInQuotedContent", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_ORDERED_LIST, "InsertOrderedList", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_PARAGRAPH, "InsertParagraph", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, "InsertText", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_INSERT_UNORDERED_LIST, "InsertUnorderedList", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_ITALIC, "Italic", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER, "JustifyCenter", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_FULL, "JustifyFull", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_LEFT, "JustifyLeft", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_NONE, "JustifyNone", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT, "JustifyRight", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_OUTDENT, "Outdent", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PASTE, "Paste", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PASTE_AND_MATCH_STYLE, "PasteAndMatchStyle", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PASTE_AS_PLAIN_TEXT, "PasteAsPlainText", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_PRINT, "Print", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_REDO, "Redo", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_REMOVE_FORMAT, "RemoveFormat", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_SELECT_ALL, "SelectAll", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH, "Strikethrough", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS, "StyleWithCSS", TRUE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT, "Subscript", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT, "Superscript", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_TRANSPOSE, "Transpose", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE, "Underline", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNDO, "Undo", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNLINK, "Unlink", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_UNSELECT, "Unselect", FALSE)
+               CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_USE_CSS, "UseCSS", TRUE)
+       }
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       return webkit_dom_document_exec_command (
+               document, cmd_str, FALSE, has_value ? value : "" );
+}
+
+/**
+ * e_html_editor_view_get_changed:
+ * @view: an #EHTMLEditorView
+ *
+ * Whether content of the editor has been changed.
+ *
+ * Returns: @TRUE when document was changed, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_get_changed (EHTMLEditorView *view)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+       return view->priv->changed;
+}
+
+/**
+ * e_html_editor_view_set_changed:
+ * @view: an #EHTMLEditorView
+ * @changed: whether document has been changed or not
+ *
+ * Sets whether document has been changed or not. The editor is tracking changes
+ * automatically, but sometimes it's necessary to change the dirty flag to force
+ * "Save changes" dialog for example.
+ */
+void
+e_html_editor_view_set_changed (EHTMLEditorView *view,
+                                gboolean changed)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+       if (view->priv->changed == changed)
+               return;
+
+       view->priv->changed = changed;
+
+       g_object_notify (G_OBJECT (view), "changed");
+}
+
+static gboolean
+is_citation_node (WebKitDOMNode *node)
+{
+       char *value;
+
+       if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node))
+               return FALSE;
+
+       value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
+
+       /* citation == <blockquote type='cite'> */
+       if (g_strcmp0 (value, "cite") == 0) {
+               g_free (value);
+               return TRUE;
+       } else {
+               g_free (value);
+               return FALSE;
+       }
+}
+
+static gchar *
+get_quotation_for_level (gint quote_level)
+{
+       gint ii;
+       GString *output = g_string_new ("");
+
+       for (ii = 0; ii < quote_level; ii++) {
+               g_string_append (output, "<span class=\"quote_character\">");
+               g_string_append (output, QUOTE_SYMBOL);
+               g_string_append (output, " ");
+               g_string_append (output, "</span>");
+       }
+
+       return g_string_free (output, FALSE);
+}
+
+static void
+insert_quote_symbols (WebKitDOMHTMLElement *element,
+                      gint quote_level,
+                      gboolean skip_first,
+                      gboolean insert_newline)
+{
+       gchar *text;
+       gint ii;
+       GString *output;
+       gchar *quotation;
+
+       if (!WEBKIT_DOM_IS_HTML_ELEMENT (element))
+               return;
+
+       text = webkit_dom_html_element_get_inner_html (element);
+       output = g_string_new ("");
+       quotation = get_quotation_for_level (quote_level);
+
+       if (g_strcmp0 (text, "\n") == 0) {
+               g_string_append (output, "<span class=\"-x-evo-quoted\">");
+               g_string_append (output, quotation);
+               g_string_append (output, "</span>");
+               g_string_append (output, "\n");
+       } else {
+               gchar **lines;
+
+               lines = g_strsplit (text, "\n", 0);
+
+               for (ii = 0; lines[ii]; ii++) {
+                       if (ii == 0 && skip_first) {
+                               if (g_strv_length (lines) == 1) {
+                                       g_strfreev (lines);
+                                       goto exit;
+                               }
+                               g_string_append (output, lines[ii]);
+                               g_string_append (output, "\n");
+                       }
+
+                       g_string_append (output, "<span class=\"-x-evo-quoted\">");
+                       g_string_append (output, quotation);
+                       g_string_append (output, "</span>");
+
+                       /* Insert line of text */
+                       g_string_append (output, lines[ii]);
+                       if ((ii == g_strv_length (lines) - 1) &&
+                           !g_str_has_suffix (text, "\n") && !insert_newline) {
+                               /* If we are on last line and node's text doesn't
+                                * end with \n, don't insert it */
+                               break;
+                       }
+                       g_string_append (output, "\n");
+               }
+
+               g_strfreev (lines);
+       }
+
+       webkit_dom_html_element_set_inner_html (element, output->str, NULL);
+ exit:
+       g_free (quotation);
+       g_free (text);
+       g_string_free (output, TRUE);
+}
+
+static void
+quote_node (WebKitDOMDocument *document,
+           WebKitDOMNode *node,
+           gint quote_level)
+{
+       gboolean skip_first = FALSE;
+       gboolean insert_newline = FALSE;
+       gboolean is_html_node = FALSE;
+       WebKitDOMElement *wrapper;
+       WebKitDOMNode *node_clone, *prev_sibling, *next_sibling;
+
+       /* Don't quote when we are not in citation */
+       if (quote_level == 0)
+               return;
+
+       if (WEBKIT_DOM_IS_COMMENT (node))
+               return;
+
+       if (WEBKIT_DOM_IS_HTML_ELEMENT (node)) {
+               insert_quote_symbols (
+                       WEBKIT_DOM_HTML_ELEMENT (node), quote_level, FALSE, FALSE);
+               return;
+       }
+
+       prev_sibling = webkit_dom_node_get_previous_sibling (node);
+       next_sibling = webkit_dom_node_get_next_sibling (node);
+
+       is_html_node =
+               !WEBKIT_DOM_IS_COMMENT (prev_sibling) && (
+               WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling) ||
+               element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "b") ||
+               element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "i") ||
+               element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "u"));
+
+       if (prev_sibling && is_html_node)
+               skip_first = TRUE;
+
+       /* Skip the BR between first blockquote and pre */
+       if (quote_level == 1 && next_sibling && WEBKIT_DOM_IS_HTML_PRE_ELEMENT (next_sibling))
+               return;
+
+       if (next_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling) &&
+           WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (webkit_dom_node_get_next_sibling (next_sibling))) {
+               insert_newline = TRUE;
+       }
+
+       /* Do temporary wrapper */
+       wrapper = webkit_dom_document_create_element (document, "SPAN", NULL);
+       webkit_dom_element_set_class_name (wrapper, "-x-evo-temp-text-wrapper");
+
+       node_clone = webkit_dom_node_clone_node (node, TRUE);
+
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (wrapper),
+               node_clone,
+               NULL);
+
+       insert_quote_symbols (
+               WEBKIT_DOM_HTML_ELEMENT (wrapper),
+               quote_level,
+               skip_first,
+               insert_newline);
+
+       webkit_dom_node_replace_child (
+               webkit_dom_node_get_parent_node (node),
+               WEBKIT_DOM_NODE (wrapper),
+               node,
+               NULL);
+}
+
+static void
+insert_quote_symbols_before_node (WebKitDOMDocument *document,
+                                  WebKitDOMNode *node,
+                                  gint quote_level,
+                                  gboolean is_html_node)
+{
+       gchar *quotation;
+       WebKitDOMElement *element;
+
+       quotation = get_quotation_for_level (quote_level);
+       element = webkit_dom_document_create_element (document, "SPAN", NULL);
+       element_add_class (element, "-x-evo-quoted");
+       webkit_dom_html_element_set_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (element), quotation, NULL);
+
+       if (is_html_node) {
+               WebKitDOMElement *new_br;
+
+               new_br = webkit_dom_document_create_element (document, "br", NULL);
+               element_add_class (new_br, "-x-evo-temp-br");
+
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (new_br),
+                       node,
+                       NULL);
+       }
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (node),
+               WEBKIT_DOM_NODE (element),
+               node,
+               NULL);
+
+       if (is_html_node) {
+               webkit_dom_node_remove_child (
+                       webkit_dom_node_get_parent_node (node),
+                       node,
+                       NULL);
+       }
+
+       g_free (quotation);
+}
+
+static void
+quote_plain_text_recursive (WebKitDOMDocument *document,
+                           WebKitDOMNode *node,
+                           WebKitDOMNode *start_node,
+                           gint quote_level)
+{
+       gboolean skip_node = FALSE;
+       gboolean move_next = FALSE;
+       gboolean suppress_next = FALSE;
+       gboolean is_html_node = FALSE;
+       WebKitDOMNode *next_sibling, *prev_sibling;
+
+       node = webkit_dom_node_get_first_child (node);
+
+       while (node) {
+               skip_node = FALSE;
+               move_next = FALSE;
+               is_html_node = FALSE;
+
+               if (WEBKIT_DOM_IS_COMMENT (node))
+                       goto next_node;
+
+               prev_sibling = webkit_dom_node_get_previous_sibling (node);
+               next_sibling = webkit_dom_node_get_next_sibling (node);
+
+               if (WEBKIT_DOM_IS_TEXT (node)) {
+                       /* Start quoting after we are in blockquote */
+                       if (quote_level > 0 && !suppress_next) {
+                               /* When quoting text node, we are wrappering it and
+                                * afterwards replacing it with that wrapper, thus asking
+                                * for next_sibling after quoting will return NULL bacause
+                                * that node don't exist anymore */
+                               quote_node (document, node, quote_level);
+                               node = next_sibling;
+                               skip_node = TRUE;
+                       } else
+                               suppress_next = FALSE;
+
+                       goto next_node;
+               }
+
+               if (!(WEBKIT_DOM_IS_ELEMENT (node) || WEBKIT_DOM_IS_HTML_ELEMENT (node)))
+                       goto next_node;
+
+               if (element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-caret-position")) {
+                       if (quote_level > 0)
+                               element_add_class (
+                                       WEBKIT_DOM_ELEMENT (node), "-x-evo-caret-quoting");
+
+                       move_next = TRUE;
+                       suppress_next = TRUE;
+                       goto next_node;
+               }
+
+               if (element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-selection-start-marker") ||
+                   element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-selection-end-marker")) {
+                       move_next = TRUE;
+                       suppress_next = TRUE;
+                       goto next_node;
+               }
+
+               if (WEBKIT_DOM_IS_HTML_META_ELEMENT (node)) {
+                       goto next_node;
+               }
+               if (WEBKIT_DOM_IS_HTML_STYLE_ELEMENT (node)) {
+                       move_next = TRUE;
+                       goto next_node;
+               }
+               if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (node)) {
+                       move_next = TRUE;
+                       goto next_node;
+               }
+
+               if (webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (node)) != 0)
+                       goto with_children;
+
+               /* Even in plain text mode we can have some basic html element
+                * like anchor and others. When Forwaring e-mail as Quoted EMFormat
+                * generates header that contatains <b> tags (bold font).
+                * We have to treat these elements separately to avoid
+                * modifications of theirs inner texts */
+               is_html_node =
+                       WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) ||
+                       element_has_tag (WEBKIT_DOM_ELEMENT (node), "b") ||
+                       element_has_tag (WEBKIT_DOM_ELEMENT (node), "i") ||
+                       element_has_tag (WEBKIT_DOM_ELEMENT (node), "u");
+
+               if (is_html_node) {
+                       if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling))
+                               insert_quote_symbols_before_node (
+                                       document, prev_sibling, quote_level, TRUE);
+
+                       move_next = TRUE;
+                       goto next_node;
+               }
+
+               /* If element doesn't have children, we can quote it */
+               if (is_citation_node (node)) {
+                       /* Citation with just text inside */
+                       quote_node (document, node, quote_level + 1);
+                       /* Set citation as quoted */
+                       element_add_class (
+                               WEBKIT_DOM_ELEMENT (node),
+                               "-x-evo-plaintext-quoted");
+
+                       move_next = TRUE;
+                       goto next_node;
+               }
+
+               if (!WEBKIT_DOM_IS_HTMLBR_ELEMENT (node))
+                       goto not_br;
+
+               if (!prev_sibling) {
+                       WebKitDOMNode *parent;
+
+                       parent = webkit_dom_node_get_parent_node (node);
+
+                       /* BR in the beginning of the citation */
+                       if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent))
+                               insert_quote_symbols_before_node (
+                                       document, node, quote_level, FALSE);
+               }
+
+               if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) &&
+                   WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (next_sibling) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-temp-text-wrapper")) {
+                       /* Situation when anchors are alone on line */
+                       gchar *text_content;
+
+                       text_content = webkit_dom_node_get_text_content (prev_sibling);
+
+                       if (g_str_has_suffix (text_content, "\n")) {
+                               insert_quote_symbols_before_node (
+                                       document, node, quote_level, FALSE);
+                               webkit_dom_node_remove_child (
+                                       webkit_dom_node_get_parent_node (node),
+                                       node,
+                                       NULL);
+                               g_free (text_content);
+                               node = next_sibling;
+                               skip_node = TRUE;
+                               goto next_node;
+                       }
+                       g_free (text_content);
+               }
+
+               if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) {
+                       gchar *quotation, *content;
+
+                       quotation = get_quotation_for_level (quote_level);
+
+                       content = g_strconcat (
+                               "<span class=\"-x-evo-quoted\">",
+                               quotation,
+                               "</span><br class=\"-x-evo-temp-br\">",
+                               NULL);
+
+                       webkit_dom_html_element_set_outer_html (
+                               WEBKIT_DOM_HTML_ELEMENT (node),
+                               content,
+                               NULL);
+
+                       g_free (content);
+                       g_free (quotation);
+
+                       node = next_sibling;
+                       skip_node = TRUE;
+                       goto next_node;
+               }
+
+               if (!prev_sibling && !next_sibling) {
+                       WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+
+                       if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent)) {
+                               insert_quote_symbols_before_node (
+                                       document, node, quote_level, FALSE);
+                       }
+               }
+
+               if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-temp-text-wrapper")) {
+                       gchar *text_content;
+
+                       text_content = webkit_dom_node_get_text_content (prev_sibling);
+                       if (g_strcmp0 (text_content, "") == 0)
+                               insert_quote_symbols_before_node (
+                                       document, node, quote_level, FALSE);
+
+                       g_free (text_content);
+               }
+
+               if (is_citation_node (prev_sibling)) {
+                       insert_quote_symbols_before_node (
+                               document, node, quote_level, FALSE);
+               }
+ not_br:
+               if (g_strcmp0 (webkit_dom_node_get_text_content (node), "") == 0) {
+                       move_next = TRUE;
+                       goto next_node;
+               }
+
+               quote_node (document, node, quote_level);
+
+               move_next = TRUE;
+               goto next_node;
+
+ with_children:
+               if (is_citation_node (node)) {
+                       /* Go deeper and increase level */
+                       quote_plain_text_recursive (
+                               document, node, start_node, quote_level + 1);
+                       /* set citation as quoted */
+                       element_add_class (
+                               WEBKIT_DOM_ELEMENT (node),
+                               "-x-evo-plaintext-quoted");
+                       move_next = TRUE;
+               } else {
+                       quote_plain_text_recursive (
+                               document, node, start_node, quote_level);
+                       move_next = TRUE;
+               }
+ next_node:
+               if (!skip_node) {
+                       /* Move to next node */
+                       if (!move_next && webkit_dom_node_has_child_nodes (node)) {
+                               node = webkit_dom_node_get_first_child (node);
+                       } else if (webkit_dom_node_get_next_sibling (node)) {
+                               node = webkit_dom_node_get_next_sibling (node);
+                       } else {
+                               return;
+                       }
+               }
+       }
+}
+
+static gint
+get_citation_level (WebKitDOMNode *node,
+                    gboolean set_plaintext_quoted)
+{
+       WebKitDOMNode *parent = node;
+       gint level = 0;
+
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
+                   webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type")) {
+                       level++;
+
+                       if (set_plaintext_quoted) {
+                               element_add_class (
+                                       WEBKIT_DOM_ELEMENT (parent),
+                                       "-x-evo-plaintext-quoted");
+                       }
+               }
+
+               parent = webkit_dom_node_get_parent_node (parent);
+       }
+
+       return level;
+}
+
+WebKitDOMElement *
+e_html_editor_view_quote_plain_text_element (EHTMLEditorView *view,
+                                             WebKitDOMElement *element)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *element_clone;
+       WebKitDOMNodeList *list;
+       gint ii, length, level;
+
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
+
+       element_clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (element), TRUE);
+       level = get_citation_level (WEBKIT_DOM_NODE (element), TRUE);
+
+       /* Remove old quote characters if the exists */
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (element_clone), "span.-x-evo-quoted", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for  (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               webkit_dom_node_remove_child (
+                       webkit_dom_node_get_parent_node (node), node, NULL);
+       }
+
+       quote_plain_text_recursive (
+               document, element_clone, element_clone, level);
+
+       /* Replace old element with one, that is quoted */
+       webkit_dom_node_replace_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+               element_clone,
+               WEBKIT_DOM_NODE (element),
+               NULL);
+
+       return WEBKIT_DOM_ELEMENT (element_clone);
+}
+
+/**
+ * e_html_editor_view_quote_plain_text:
+ * @view: an #EHTMLEditorView
+ *
+ * Quote text inside citation blockquotes in plain text mode.
+ *
+ * As this function is cloning and replacing all citation blockquotes keep on
+ * mind that any pointers to nodes inside these blockquotes will be invalidated.
+ */
+WebKitDOMElement *
+e_html_editor_view_quote_plain_text (EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNode *body_clone;
+       WebKitDOMNamedNodeMap *attributes;
+       WebKitDOMNodeList *list;
+       WebKitDOMElement *element;
+       gint ii, length;
+       gulong attributes_length;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       /* Check if the document is already quoted */
+       element = webkit_dom_document_query_selector (
+               document, ".-x-evo-plaintext-quoted", NULL);
+       if (element)
+               return NULL;
+
+       body = webkit_dom_document_get_body (document);
+       body_clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), TRUE);
+
+       /* Clean unwanted spaces before and after blockquotes */
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (body_clone), "blockquote[type|=cite]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *blockquote = webkit_dom_node_list_item (list, ii);
+               WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (blockquote);
+               WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (blockquote);
+
+               if (prev_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) {
+                       webkit_dom_node_remove_child (
+                               webkit_dom_node_get_parent_node (prev_sibling),
+                               prev_sibling,
+                               NULL);
+               }
+               if (next_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling)) {
+                       webkit_dom_node_remove_child (
+                               webkit_dom_node_get_parent_node (next_sibling),
+                               next_sibling,
+                               NULL);
+               }
+               if (webkit_dom_node_has_child_nodes (blockquote)) {
+                       WebKitDOMNode *child = webkit_dom_node_get_first_child (blockquote);
+                       if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) {
+                               webkit_dom_node_remove_child (
+                                       blockquote,
+                                       child,
+                                       NULL);
+                       }
+               }
+       }
+
+       quote_plain_text_recursive (document, body_clone, body_clone, 0);
+
+       /* Copy attributes */
+       attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+       attributes_length = webkit_dom_named_node_map_get_length (attributes);
+       for (ii = 0; ii < attributes_length; ii++) {
+               gchar *name, *value;
+               WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+               name = webkit_dom_node_get_local_name (node);
+               value = webkit_dom_node_get_node_value (node);
+
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (body_clone), name, value, NULL);
+
+               g_free (name);
+               g_free (value);
+       }
+
+       /* Replace old BODY with one, that is quoted */
+       webkit_dom_node_replace_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body)),
+               body_clone,
+               WEBKIT_DOM_NODE (body),
+               NULL);
+
+       return WEBKIT_DOM_ELEMENT (body_clone);
+}
+
+/**
+ * e_html_editor_view_dequote_plain_text:
+ * @view: an #EHTMLEditorView
+ *
+ * Dequote already quoted plain text in editor.
+ * Editor have to be quoted with e_html_editor_view_quote_plain_text otherwise
+ * it's not working.
+ */
+void
+e_html_editor_view_dequote_plain_text (EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *paragraphs;
+       gint length, ii;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       paragraphs = webkit_dom_document_query_selector_all (
+               document, "blockquote.-x-evo-plaintext-quoted", NULL);
+       length = webkit_dom_node_list_get_length (paragraphs);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNodeList *list;
+               WebKitDOMElement *element;
+               gint jj, list_length;
+
+               element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (paragraphs, ii));
+
+               if (is_citation_node (WEBKIT_DOM_NODE (element))) {
+                       element_remove_class (element, "-x-evo-plaintext-quoted");
+
+                       list = webkit_dom_element_query_selector_all (
+                               element, "span.-x-evo-quoted", NULL);
+                       list_length = webkit_dom_node_list_get_length (list);
+                       for (jj = 0; jj < list_length; jj++) {
+                               WebKitDOMNode *node = webkit_dom_node_list_item (list, jj);
+
+                               webkit_dom_node_remove_child (
+                                       webkit_dom_node_get_parent_node (node),
+                                       node,
+                                       NULL);
+                       }
+                       list = webkit_dom_element_query_selector_all (
+                               element, "span.-x-evo-temp-text-wrapper", NULL);
+                       list_length = webkit_dom_node_list_get_length (list);
+                       for (jj = 0; jj < list_length; jj++) {
+                               WebKitDOMNode *node = webkit_dom_node_list_item (list, jj);
+
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (node),
+                                       webkit_dom_node_get_first_child (node),
+                                       node,
+                                       NULL);
+                       }
+               }
+       }
+}
+
+/**
+ * e_html_editor_view_get_html_mode:
+ * @view: an #EHTMLEditorView
+ *
+ * Whether the editor is in HTML mode or plain text mode. In HTML mode,
+ * more formatting options are avilable an the email is sent as
+ * multipart/alternative.
+ *
+ * Returns: @TRUE when HTML mode is enabled, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_get_html_mode (EHTMLEditorView *view)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+       return view->priv->html_mode;
+}
+
+static gint
+get_indentation_level (WebKitDOMElement *element)
+{
+       WebKitDOMElement *parent;
+       gint level = 1;
+
+       parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element));
+       /* Count level of indentation */
+       while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               if (element_has_class (parent, "-x-evo-indented"))
+                       level++;
+
+               parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (parent));
+       }
+
+       return level;
+}
+
+static void
+process_blockquote (WebKitDOMElement *blockquote)
+{
+       WebKitDOMNodeList *list;
+       int jj, length;
+
+       /* First replace wrappers */
+       list = webkit_dom_element_query_selector_all (
+               blockquote, "span.-x-evo-temp-text-wrapper", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (jj = 0; jj < length; jj++) {
+               WebKitDOMNode *quoted_node;
+               gchar *text_content, *tmp = NULL;
+
+               quoted_node = webkit_dom_node_list_item (list, jj);
+               text_content = webkit_dom_node_get_text_content (quoted_node);
+               if (webkit_dom_node_get_previous_sibling (quoted_node)) {
+                       tmp = g_strconcat ("<br>", text_content, NULL);
+                       g_free (text_content);
+                       text_content = tmp;
+               }
+
+               webkit_dom_html_element_set_outer_html (
+                       WEBKIT_DOM_HTML_ELEMENT (quoted_node), text_content, NULL);
+
+               g_free (text_content);
+       }
+
+       /* Afterwards replace quote nodes with symbols */
+       list = webkit_dom_element_query_selector_all (
+               blockquote, "span.-x-evo-quoted", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (jj = 0; jj < length; jj++) {
+               WebKitDOMNode *quoted_node;
+               gchar *text_content, *tmp = NULL;
+
+               quoted_node = webkit_dom_node_list_item (list, jj);
+               text_content = webkit_dom_node_get_text_content (quoted_node);
+               if (webkit_dom_node_get_previous_sibling (quoted_node)) {
+                       tmp = g_strconcat ("<br>", text_content, NULL);
+                       g_free (text_content);
+                       text_content = tmp;
+               }
+
+               webkit_dom_html_element_set_outer_html (
+                       WEBKIT_DOM_HTML_ELEMENT (quoted_node), text_content, NULL);
+
+               g_free (text_content);
+       }
+
+       if (element_has_class (blockquote, "-x-evo-indented")) {
+               WebKitDOMNode *child;
+               gchar *spaces;
+
+               spaces = g_strnfill (4 * get_indentation_level (blockquote), ' ');
+
+               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (blockquote));
+               while (child) {
+                       /* If next sibling is indented blockqoute skip it,
+                        * it will be processed afterwards */
+                       if (WEBKIT_DOM_IS_ELEMENT (child) &&
+                           element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-indented"))
+                               child = webkit_dom_node_get_next_sibling (child);
+
+                       if (WEBKIT_DOM_IS_TEXT (child)) {
+                               gchar *text_content;
+                               gchar *indented_text;
+
+                               text_content = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (child));
+                               indented_text = g_strconcat (spaces, text_content, NULL);
+
+                               webkit_dom_text_replace_whole_text (
+                                       WEBKIT_DOM_TEXT (child),
+                                       indented_text,
+                                       NULL);
+
+                               g_free (text_content);
+                               g_free (indented_text);
+                       }
+
+                       if (!child)
+                               break;
+
+                       /* Move to next node */
+                       if (webkit_dom_node_has_child_nodes (child))
+                               child = webkit_dom_node_get_first_child (child);
+                       else if (webkit_dom_node_get_next_sibling (child))
+                               child = webkit_dom_node_get_next_sibling (child);
+                       else {
+                               if (webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (blockquote), child))
+                                       break;
+
+                               child = webkit_dom_node_get_parent_node (child);
+                               if (child)
+                                       child = webkit_dom_node_get_next_sibling (child);
+                       }
+               }
+               g_free (spaces);
+
+               webkit_dom_element_remove_attribute (blockquote, "style");
+       }
+}
+
+/* Taken from GtkHTML */
+static gchar *
+get_alpha_value (gint value,
+                 gboolean lower)
+{
+       GString *str;
+       gchar *rv;
+       gint add = lower ? 'a' : 'A';
+
+       str = g_string_new (". ");
+
+       do {
+               g_string_prepend_c (str, ((value - 1) % 26) + add);
+               value = (value - 1) / 26;
+       } while (value);
+
+       rv = str->str;
+       g_string_free (str, FALSE);
+
+       return rv;
+}
+
+/* Taken from GtkHTML */
+static gchar *
+get_roman_value (gint value,
+                 gboolean lower)
+{
+       GString *str;
+       const gchar *base = "IVXLCDM";
+       gchar *rv;
+       gint b, r, add = lower ? 'a' - 'A' : 0;
+
+       if (value > 3999)
+               return g_strdup ("?. ");
+
+       str = g_string_new (". ");
+
+       for (b = 0; value > 0 && b < 7 - 1; b += 2, value /= 10) {
+               r = value % 10;
+               if (r != 0) {
+                       if (r < 4) {
+                               for (; r; r--)
+                                       g_string_prepend_c (str, base[b] + add);
+                       } else if (r == 4) {
+                               g_string_prepend_c (str, base[b + 1] + add);
+                               g_string_prepend_c (str, base[b] + add);
+                       } else if (r == 5) {
+                               g_string_prepend_c (str, base[b + 1] + add);
+                       } else if (r < 9) {
+                               for (; r > 5; r--)
+                                       g_string_prepend_c (str, base[b] + add);
+                               g_string_prepend_c (str, base[b + 1] + add);
+                       } else if (r == 9) {
+                               g_string_prepend_c (str, base[b + 2] + add);
+                               g_string_prepend_c (str, base[b] + add);
+                       }
+               }
+       }
+
+       rv = str->str;
+       g_string_free (str, FALSE);
+
+       return rv;
+}
+
+static void
+process_list_to_plain_text (EHTMLEditorView *view,
+                            WebKitDOMElement *element,
+                            gint level,
+                            GString *output)
+{
+       EHTMLEditorSelectionBlockFormat format;
+       EHTMLEditorSelectionAlignment alignment;
+       gint counter = 1;
+       gchar *indent_per_level = g_strnfill (SPACES_PER_LIST_LEVEL, ' ');
+       WebKitDOMNode *item;
+       gint word_wrap_length = e_html_editor_selection_get_word_wrap_length (
+               e_html_editor_view_get_selection (view));
+
+       format = e_html_editor_selection_get_list_format_from_node (
+               WEBKIT_DOM_NODE (element));
+
+       /* Process list items to plain text */
+       item = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element));
+       while (item) {
+               if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (item))
+                       g_string_append (output, "\n");
+
+               if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
+                       gchar *space, *item_str = NULL;
+                       gint ii = 0;
+                       WebKitDOMElement *wrapped;
+                       GString *item_value = g_string_new ("");
+
+                       alignment = e_html_editor_selection_get_list_alignment_from_node (
+                               WEBKIT_DOM_NODE (item));
+
+                       wrapped = webkit_dom_element_query_selector (
+                               WEBKIT_DOM_ELEMENT (item), ".-x-evo-wrap-br", NULL);
+                       /* Wrapped text */
+                       if (wrapped) {
+                               WebKitDOMNode *node = webkit_dom_node_get_first_child (item);
+                               GString *line = g_string_new ("");
+                               while (node) {
+                                       if (WEBKIT_DOM_IS_TEXT (node)) {
+                                               /* append text from line */
+                                               gchar *text_content;
+                                               text_content = webkit_dom_node_get_text_content (node);
+                                               g_string_append (line, text_content);
+                                               g_free (text_content);
+                                       }
+                                       if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (node) &&
+                                           element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")) {
+                                               /* put spaces before line characters -> wordwraplength - 
indentation */
+                                               g_string_append (line, "\n");
+                                               /* put spaces before line characters -> wordwraplength - 
indentation */
+                                               for (ii = 0; ii < level; ii++)
+                                                       g_string_append (line, indent_per_level);
+                                               g_string_append (item_value, line->str);
+                                               g_string_erase (line, 0, -1);
+                                       }
+                                       node = webkit_dom_node_get_next_sibling (node);
+                               }
+
+                               if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT)
+                                       g_string_append (item_value, line->str);
+
+                               if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER) {
+                                       gchar *fill = NULL;
+                                       gint fill_length;
+
+                                       fill_length = word_wrap_length - g_utf8_strlen (line->str, -1);
+                                       fill_length -= ii * SPACES_PER_LIST_LEVEL;
+                                       fill_length /= 2;
+
+                                       if (fill_length < 0)
+                                               fill_length = 0;
+
+                                       fill = g_strnfill (fill_length, ' ');
+
+                                       g_string_append (item_value, fill);
+                                       g_string_append (item_value, line->str);
+                                       g_free (fill);
+                               }
+
+                               if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT) {
+                                       gchar *fill = NULL;
+                                       gint fill_length;
+
+                                       fill_length = word_wrap_length - g_utf8_strlen (line->str, -1);
+                                       fill_length -= ii * SPACES_PER_LIST_LEVEL;
+
+                                       if (fill_length < 0)
+                                               fill_length = 0;
+
+                                       fill = g_strnfill (fill_length, ' ');
+
+                                       g_string_append (item_value, fill);
+                                       g_string_append (item_value, line->str);
+                                       g_free (fill);
+                               }
+                               g_string_free (line, TRUE);
+                               /* that same here */
+                       } else {
+                               gchar *text_content =
+                                       webkit_dom_node_get_text_content (item);
+                               g_string_append (item_value, text_content);
+                               g_free (text_content);
+                       }
+
+                       if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST) {
+                               space = g_strnfill (SPACES_PER_LIST_LEVEL - 2, ' ');
+                               item_str = g_strdup_printf (
+                                       "%s* %s", space, item_value->str);
+                               g_free (space);
+                       }
+
+                       if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST) {
+                               gint length = 1, tmp = counter;
+
+                               while ((tmp = tmp / 10) > 1)
+                                       length++;
+
+                               if (tmp == 1)
+                                       length++;
+
+                               space = g_strnfill (SPACES_PER_LIST_LEVEL - 2 - length, ' ');
+                               item_str = g_strdup_printf (
+                                       "%s%d. %s", space, counter, item_value->str);
+                               g_free (space);
+                       }
+
+                       if (format > E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST) {
+                               gchar *value;
+
+                               if (format == E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA)
+                                       value = get_alpha_value (counter, FALSE);
+                               else
+                                       value = get_roman_value (counter, FALSE);
+
+                               /* Value already containes dot and space */
+                               space = g_strnfill (SPACES_PER_LIST_LEVEL - strlen (value), ' ');
+                               item_str = g_strdup_printf (
+                                       "%s%s%s", space, value, item_value->str);
+                               g_free (space);
+                               g_free (value);
+                       }
+
+                       if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT) {
+                               for (ii = 0; ii < level - 1; ii++) {
+                                       g_string_append (output, indent_per_level);
+                               }
+                               g_string_append (output, item_str);
+                       }
+
+                       if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT) {
+                               if (!wrapped) {
+                                       gchar *fill = NULL;
+                                       gint fill_length;
+
+                                       fill_length = word_wrap_length - g_utf8_strlen (item_str, -1);
+                                       fill_length -= ii * SPACES_PER_LIST_LEVEL;
+
+                                       if (fill_length < 0)
+                                               fill_length = 0;
+
+                                       if (g_str_has_suffix (item_str, " "))
+                                               fill_length++;
+
+                                       fill = g_strnfill (fill_length, ' ');
+
+                                       g_string_append (output, fill);
+                                       g_free (fill);
+                               }
+                               if (g_str_has_suffix (item_str, " "))
+                                       g_string_append_len (output, item_str, g_utf8_strlen (item_str, -1) - 
1);
+                               else
+                                       g_string_append (output, item_str);
+                       }
+
+                       if (alignment == E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER) {
+                               if (!wrapped) {
+                                       gchar *fill = NULL;
+                                       gint fill_length = 0;
+
+                                       for (ii = 0; ii < level - 1; ii++)
+                                               g_string_append (output, indent_per_level);
+
+                                       fill_length = word_wrap_length - g_utf8_strlen (item_str, -1);
+                                       fill_length -= ii * SPACES_PER_LIST_LEVEL;
+                                       fill_length /= 2;
+
+                                       if (fill_length < 0)
+                                               fill_length = 0;
+
+                                       if (g_str_has_suffix (item_str, " "))
+                                               fill_length++;
+
+                                       fill = g_strnfill (fill_length, ' ');
+
+                                       g_string_append (output, fill);
+                                       g_free (fill);
+                               }
+                               if (g_str_has_suffix (item_str, " "))
+                                       g_string_append_len (output, item_str, g_utf8_strlen (item_str, -1) - 
1);
+                               else
+                                       g_string_append (output, item_str);
+                       }
+
+                       counter++;
+                       item = webkit_dom_node_get_next_sibling (item);
+                       if (item)
+                               g_string_append (output, "\n");
+
+                       g_free (item_str);
+                       g_string_free (item_value, TRUE);
+               } else if (WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (item) ||
+                          WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (item)) {
+                       process_list_to_plain_text (
+                               view, WEBKIT_DOM_ELEMENT (item), level + 1, output);
+                       item = webkit_dom_node_get_next_sibling (item);
+               } else {
+                       item = webkit_dom_node_get_next_sibling (item);
+               }
+       }
+
+       if (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)))
+               g_string_append (output, "\n");
+
+       g_free (indent_per_level);
+}
+
+static void
+remove_base_attributes (WebKitDOMElement *element)
+{
+       webkit_dom_element_remove_attribute (element, "class");
+       webkit_dom_element_remove_attribute (element, "id");
+       webkit_dom_element_remove_attribute (element, "name");
+}
+
+static void
+remove_evolution_attributes (WebKitDOMElement *element)
+{
+       webkit_dom_element_remove_attribute (element, "x-evo-smiley");
+       webkit_dom_element_remove_attribute (element, "data-converted");
+       webkit_dom_element_remove_attribute (element, "data-edit-as-new");
+       webkit_dom_element_remove_attribute (element, "data-inline");
+       webkit_dom_element_remove_attribute (element, "data-uri");
+       webkit_dom_element_remove_attribute (element, "data-message");
+       webkit_dom_element_remove_attribute (element, "data-name");
+       webkit_dom_element_remove_attribute (element, "data-new-message");
+       webkit_dom_element_remove_attribute (element, "spellcheck");
+}
+/*
+static void
+remove_style_attributes (WebKitDOMElement *element)
+{
+       webkit_dom_element_remove_attribute (element, "bgcolor");
+       webkit_dom_element_remove_attribute (element, "background");
+       webkit_dom_element_remove_attribute (element, "style");
+}
+*/
+static void
+process_elements (EHTMLEditorView *view,
+                  WebKitDOMNode *node,
+                  gboolean to_html,
+                  gboolean changing_mode,
+                  gboolean to_plain_text,
+                  GString *buffer)
+{
+       WebKitDOMNodeList *nodes;
+       gulong ii, length;
+       gchar *content;
+       gboolean skip_nl = FALSE;
+
+       if (to_plain_text && !buffer)
+               return;
+
+       if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) {
+               if (changing_mode && to_plain_text) {
+                       WebKitDOMNamedNodeMap *attributes;
+                       gulong attributes_length;
+
+                       /* Copy attributes */
+                       g_string_append (buffer, "<html><head></head><body ");
+                       attributes = webkit_dom_element_get_attributes (
+                               WEBKIT_DOM_ELEMENT (node));
+                       attributes_length =
+                               webkit_dom_named_node_map_get_length (attributes);
+
+                       for (ii = 0; ii < attributes_length; ii++) {
+                               gchar *name, *value;
+                               WebKitDOMNode *node =
+                                       webkit_dom_named_node_map_item (
+                                               attributes, ii);
+
+                               name = webkit_dom_node_get_local_name (node);
+                               value = webkit_dom_node_get_node_value (node);
+
+                               g_string_append (buffer, name);
+                               g_string_append (buffer, "=\"");
+                               g_string_append (buffer, value);
+                               g_string_append (buffer, "\" ");
+
+                               g_free (name);
+                               g_free (value);
+                       }
+                       g_string_append (buffer, ">");
+               }
+               if (to_html)
+                       remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node));
+       }
+
+       nodes = webkit_dom_node_get_child_nodes (node);
+       length = webkit_dom_node_list_get_length (nodes);
+
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *child;
+               gboolean skip_node = FALSE;
+
+               child = webkit_dom_node_list_item (nodes, ii);
+
+               if (WEBKIT_DOM_IS_TEXT (child)) {
+                       gchar *content, *tmp;
+                       GRegex *regex;
+
+                       content = webkit_dom_node_get_text_content (child);
+                       /* Replace tabs with 4 whitespaces, otherwise they got
+                        * replaced by single whitespace */
+                       if (strstr (content, "\x9")) {
+                               regex = g_regex_new ("\x9", 0, 0, NULL);
+                               tmp = g_regex_replace (
+                                       regex, content, -1, 0, "    ", 0, NULL);
+                               webkit_dom_node_set_text_content (child, tmp, NULL);
+                               g_free (content);
+                               g_free (tmp);
+                               content = webkit_dom_node_get_text_content (child);
+                               g_regex_unref (regex);
+                       }
+
+                       if (strstr (content, UNICODE_ZERO_WIDTH_SPACE)) {
+                               regex = g_regex_new (UNICODE_ZERO_WIDTH_SPACE, 0, 0, NULL);
+                               tmp = g_regex_replace (
+                                       regex, content, -1, 0, "", 0, NULL);
+                               webkit_dom_node_set_text_content (child, tmp, NULL);
+                               g_free (tmp);
+                               g_free (content);
+                               content = webkit_dom_node_get_text_content (child);
+                               g_regex_unref (regex);
+                       }
+
+                       if (to_plain_text && !changing_mode) {
+                               gchar *style;
+                               const gchar *css_align;
+
+                               style = webkit_dom_element_get_attribute (
+                                       WEBKIT_DOM_ELEMENT (node), "style");
+
+                               if ((css_align = strstr (style, "text-align: "))) {
+                                       gchar *align;
+                                       gchar *content_with_align;
+                                       gint length;
+                                       gint word_wrap_length =
+                                               e_html_editor_selection_get_word_wrap_length (
+                                                       e_html_editor_view_get_selection (view));
+
+                                       if (!g_str_has_prefix (css_align + 12, "left")) {
+                                               if (g_str_has_prefix (css_align + 12, "center"))
+                                                       length = (word_wrap_length - g_utf8_strlen (content, 
-1)) / 2;
+                                               else
+                                                       length = word_wrap_length - g_utf8_strlen (content, 
-1);
+
+                                               if (g_str_has_suffix (content, " ")) {
+                                                       char *tmp;
+
+                                                       length++;
+                                                       align = g_strnfill (length, ' ');
+
+                                                       tmp = g_strndup (content, g_utf8_strlen (content, -1) 
-1);
+
+                                                       content_with_align = g_strconcat (
+                                                               align, tmp, NULL);
+                                                       g_free (tmp);
+                                               } else {
+                                                       align = g_strnfill (length, ' ');
+
+                                                       content_with_align = g_strconcat (
+                                                               align, content, NULL);
+                                               }
+
+                                               g_free (content);
+                                               g_free (align);
+                                               content = content_with_align;
+                                       }
+                               }
+
+                               g_free (style);
+                       }
+
+                       if (to_plain_text || changing_mode)
+                               g_string_append (buffer, content);
+
+                       g_free (content);
+
+                       goto next;
+               }
+
+               if (WEBKIT_DOM_IS_COMMENT (child) || !WEBKIT_DOM_IS_ELEMENT (child))
+                       goto next;
+
+               /* Leave caret position untouched */
+               if (element_has_id (WEBKIT_DOM_ELEMENT (child), "-x-evo-caret-position")) {
+                       if (changing_mode && to_plain_text) {
+                               content = webkit_dom_html_element_get_outer_html (
+                                       WEBKIT_DOM_HTML_ELEMENT (child));
+                               g_string_append (buffer, content);
+                               g_free (content);
+                       }
+                       if (to_html) {
+                               webkit_dom_node_remove_child (
+                                       webkit_dom_node_get_parent_node (child),
+                                       child,
+                                       NULL);
+                       }
+
+                       skip_node = TRUE;
+               }
+
+               if (element_has_class (WEBKIT_DOM_ELEMENT (child), "Apple-tab-span")) {
+                       if (!changing_mode && to_plain_text) {
+                               gchar *content, *tmp;
+                               GRegex *regex;
+
+                               content = webkit_dom_node_get_text_content (child);
+                               /* Replace tabs with 4 whitespaces, otherwise they got
+                                * replaced by single whitespace */
+                               if (strstr (content, "\x9")) {
+                                       regex = g_regex_new ("\x9", 0, 0, NULL);
+                                       tmp = g_regex_replace (
+                                               regex, content, -1, 0, "    ", 0, NULL);
+                                       g_string_append (buffer, tmp);
+                                       g_free (tmp);
+                                       content = webkit_dom_node_get_text_content (child);
+                                       g_regex_unref (regex);
+                               }
+                       }
+                       if (to_html) {
+                               element_remove_class (
+                                       WEBKIT_DOM_ELEMENT (child),
+                                       "Applet-tab-span");
+                       }
+
+                       skip_node = TRUE;
+               }
+
+               /* Leave blockquotes as they are */
+               if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (child)) {
+                       if (changing_mode && to_plain_text) {
+                               content = webkit_dom_html_element_get_outer_html (
+                                       WEBKIT_DOM_HTML_ELEMENT (child));
+                               g_string_append (buffer, content);
+                               g_free (content);
+                               skip_node = TRUE;
+                       } else {
+                               if (!changing_mode && to_plain_text) {
+                                       if (get_citation_level (child, FALSE) == 0) {
+                                               gchar *value;
+                                               value = webkit_dom_element_get_attribute (
+                                                       WEBKIT_DOM_ELEMENT (child), "type");
+                                               if (value && g_strcmp0 (value, "cite") == 0) {
+                                                       g_string_append (buffer, "\n");
+                                               }
+                                               g_free (value);
+                                       }
+                               }
+                               process_blockquote (WEBKIT_DOM_ELEMENT (child));
+                       }
+               }
+
+               if (WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (child) ||
+                   WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (child)) {
+                       if (to_plain_text) {
+                               if (changing_mode) {
+                                       content = webkit_dom_html_element_get_outer_html (
+                                               WEBKIT_DOM_HTML_ELEMENT (child));
+                                       g_string_append (buffer, content);
+                                       g_free (content);
+                               } else {
+                                       process_list_to_plain_text (
+                                               view, WEBKIT_DOM_ELEMENT (child), 1, buffer);
+                               }
+                               skip_node = TRUE;
+                               goto next;
+                       }
+               }
+
+               if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-resizable-wrapper") &&
+                   !element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-smiley-wrapper")) {
+                       WebKitDOMNode *image =
+                               webkit_dom_node_get_first_child (child);
+
+                       if (to_html && WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (image)) {
+                               remove_evolution_attributes (
+                                       WEBKIT_DOM_ELEMENT (image));
+
+                               webkit_dom_node_replace_child (
+                                       node, image, child, NULL);
+                       }
+
+                       skip_node = TRUE;
+               }
+
+               /* Leave paragraphs as they are */
+               if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-paragraph")) {
+                       if (changing_mode && to_plain_text) {
+                               content = webkit_dom_html_element_get_outer_html (
+                                       WEBKIT_DOM_HTML_ELEMENT (child));
+                               g_string_append (buffer, content);
+                               g_free (content);
+                               skip_node = TRUE;
+                       }
+                       if (to_html) {
+                               remove_base_attributes (WEBKIT_DOM_ELEMENT (child));
+                               remove_evolution_attributes (WEBKIT_DOM_ELEMENT (child));
+                       }
+               }
+
+               /* Signature */
+               if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (child)) {
+                       WebKitDOMNode *first_child;
+
+                       first_child = webkit_dom_node_get_first_child (child);
+                       if (WEBKIT_DOM_IS_ELEMENT (first_child)) {
+                               if (element_has_class (
+                                       WEBKIT_DOM_ELEMENT (first_child),
+                                       "-x-evo-signature")) {
+
+                                       if (to_html) {
+                                               remove_base_attributes (
+                                                       WEBKIT_DOM_ELEMENT (first_child));
+                                               remove_evolution_attributes (
+                                                       WEBKIT_DOM_ELEMENT (first_child));
+                                       }
+                                       if (to_plain_text && !changing_mode) {
+                                               g_string_append (buffer, "\n");
+                                               content = webkit_dom_html_element_get_inner_text (
+                                                       WEBKIT_DOM_HTML_ELEMENT (first_child));
+                                               g_string_append (buffer, content);
+                                               g_free (content);
+                                               skip_nl = TRUE;
+                                       }
+                                       skip_node = TRUE;
+                               }
+                       }
+               }
+
+               /* Replace smileys with their text representation */
+               if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-smiley-wrapper")) {
+                       if (to_plain_text && !changing_mode) {
+                               WebKitDOMNode *text_version;
+
+                               text_version = webkit_dom_node_get_last_child (child);
+                               content = webkit_dom_html_element_get_inner_text (
+                                       WEBKIT_DOM_HTML_ELEMENT (text_version));
+                               g_string_append (buffer, content);
+                               g_free (content);
+                               skip_node = TRUE;
+                       }
+                       if (to_html) {
+                               WebKitDOMElement *img;
+
+                               img = WEBKIT_DOM_ELEMENT (
+                                       webkit_dom_node_get_first_child (child)),
+
+                               remove_evolution_attributes (img);
+                               remove_base_attributes (img);
+
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (child),
+                                       WEBKIT_DOM_NODE (img),
+                                       child,
+                                       NULL);
+                               webkit_dom_node_remove_child (
+                                       webkit_dom_node_get_parent_node (child),
+                                       child,
+                                       NULL);
+                               skip_node = TRUE;
+                       }
+               }
+
+               /* Leave PRE elements untouched */
+               if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (child)) {
+                       if (changing_mode && to_plain_text) {
+                               content = webkit_dom_html_element_get_outer_html (
+                                       WEBKIT_DOM_HTML_ELEMENT (child));
+                               g_string_append (buffer, content);
+                               g_free (content);
+                               skip_node = TRUE;
+                       }
+                       if (to_html)
+                               remove_evolution_attributes (WEBKIT_DOM_ELEMENT (child));
+               }
+
+               if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) {
+                       if (to_plain_text) {
+                               /* Insert new line when we hit BR element */
+                               g_string_append (buffer, changing_mode ? "<br>" : "\n");
+                       }
+               }
+ next:
+               if (webkit_dom_node_has_child_nodes (child) && !skip_node)
+                       process_elements (
+                               view, child, to_html, changing_mode, to_plain_text, buffer);
+       }
+
+       if (to_plain_text && (
+           WEBKIT_DOM_IS_HTML_DIV_ELEMENT (node) ||
+           WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (node) ||
+           WEBKIT_DOM_IS_HTML_PRE_ELEMENT (node) ||
+           WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node))) {
+
+               gboolean add_br = TRUE;
+               WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (node);
+
+               /* If we don't have next sibling (last element in body) or next element is
+                * signature we are not adding the BR element */
+               if (!next_sibling)
+                       add_br = FALSE;
+
+               if (element_has_class (webkit_dom_node_get_parent_element (node), "-x-evo-indented"))
+                       add_br = FALSE;
+
+               if (next_sibling && WEBKIT_DOM_IS_HTML_DIV_ELEMENT (next_sibling)) {
+                       if (webkit_dom_element_query_selector (
+                               WEBKIT_DOM_ELEMENT (next_sibling),
+                               "span.-x-evo-signature", NULL)) {
+
+                               add_br = FALSE;
+                       }
+               }
+
+               content = webkit_dom_node_get_text_content (node);
+               if (add_br && g_utf8_strlen (content, -1) > 0 && !skip_nl)
+                       g_string_append (buffer, changing_mode ? "<br>" : "\n");
+
+               g_free (content);
+       }
+
+}
+
+static void
+remove_wrapping (EHTMLEditorView *view)
+{
+       gint length;
+       gint ii;
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *list;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       list = webkit_dom_document_query_selector_all (document, "br.-x-evo-wrap-br", NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               webkit_dom_node_remove_child (
+                       webkit_dom_node_get_parent_node (node), node, NULL);
+       }
+}
+
+static void
+remove_images_in_element (EHTMLEditorView *view,
+                          WebKitDOMElement *element,
+                          gboolean html_mode)
+{
+       gint length, ii;
+       WebKitDOMNodeList *images;
+
+       images = webkit_dom_element_query_selector_all (
+               element, "img:not(.-x-evo-smiley-img)", NULL);
+
+       length = webkit_dom_node_list_get_length (images);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *img = webkit_dom_node_list_item (images, ii);
+
+               webkit_dom_node_remove_child (
+                       webkit_dom_node_get_parent_node (img), img, NULL);
+       }
+}
+
+static void
+remove_images (EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       remove_images_in_element (
+               view,
+               WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)),
+               view->priv->html_mode);
+}
+
+static void
+toggle_smileys (EHTMLEditorView *view)
+{
+       gboolean html_mode;
+       gint length;
+       gint ii;
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *smileys;
+
+       html_mode = e_html_editor_view_get_html_mode (view);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       smileys = webkit_dom_document_query_selector_all (
+               document, "img.-x-evo-smiley-img", NULL);
+
+       length = webkit_dom_node_list_get_length (smileys);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *img = webkit_dom_node_list_item (smileys, ii);
+               WebKitDOMNode *text = webkit_dom_node_get_next_sibling (img);
+               WebKitDOMElement *parent = webkit_dom_node_get_parent_element (img);
+
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (html_mode ? text : img),
+                       "style",
+                       "display: none",
+                       NULL);
+
+               webkit_dom_element_remove_attribute (
+                       WEBKIT_DOM_ELEMENT (html_mode ? img : text), "style");
+
+               if (html_mode)
+                       element_add_class (parent, "-x-evo-resizable-wrapper");
+               else
+                       element_remove_class (parent, "-x-evo-resizable-wrapper");
+       }
+}
+
+static void
+toggle_paragraphs_style_in_element (EHTMLEditorView *view,
+                                    WebKitDOMElement *element,
+                                   gboolean html_mode)
+{
+       EHTMLEditorSelection *selection;
+       gint ii, length;
+       WebKitDOMNodeList *paragraphs;
+
+       html_mode = e_html_editor_view_get_html_mode (view);
+       selection = e_html_editor_view_get_selection (view);
+
+       paragraphs = webkit_dom_element_query_selector_all (
+               element, ".-x-evo-paragraph", NULL);
+
+       length = webkit_dom_node_list_get_length (paragraphs);
+
+       for (ii = 0; ii < length; ii++) {
+               gchar *style;
+               const gchar *css_align;
+               WebKitDOMNode *node = webkit_dom_node_list_item (paragraphs, ii);
+
+               if (html_mode) {
+                       style = webkit_dom_element_get_attribute (
+                               WEBKIT_DOM_ELEMENT (node), "style");
+
+                       if ((css_align = strstr (style, "text-align: "))) {
+                               webkit_dom_element_set_attribute (
+                                       WEBKIT_DOM_ELEMENT (node),
+                                       "style",
+                                       g_str_has_prefix (css_align + 12, "center") ?
+                                               "text-align: center" :
+                                               "text-align: right",
+                                       NULL);
+                       } else {
+                               /* In HTML mode the paragraphs don't have width limit */
+                               webkit_dom_element_remove_attribute (
+                                       WEBKIT_DOM_ELEMENT (node), "style");
+                       }
+                       g_free (style);
+               } else {
+                       WebKitDOMNode *parent;
+
+                       parent = webkit_dom_node_get_parent_node (node);
+                       /* If the paragraph is inside indented paragraph don't set
+                        * the style as it will be inherited */
+                       if (!element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented")) {
+                               style = webkit_dom_element_get_attribute (
+                                       WEBKIT_DOM_ELEMENT (node), "style");
+
+                               if ((css_align = strstr (style, "text-align: "))) {
+                                       const gchar *style_to_add;
+
+                                       style_to_add = g_str_has_prefix (
+                                               css_align + 12, "center") ?
+                                                       "text-align: center;" :
+                                                       "text-align: right;";
+
+                                       /* In HTML mode the paragraphs have width limit */
+                                       e_html_editor_selection_set_paragraph_style (
+                                               selection, WEBKIT_DOM_ELEMENT (node),
+                                               -1, 0, style_to_add);
+                               }
+                               g_free (style);
+                       }
+               }
+       }
+}
+
+static void
+toggle_paragraphs_style (EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       toggle_paragraphs_style_in_element (
+               view,
+               WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)),
+               view->priv->html_mode);
+}
+
+static gchar *
+process_content_for_saving_as_draft (EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMElement *element, *document_element;
+       gchar *content;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-caret-position");
+
+       if (element)
+               webkit_dom_element_set_attribute (
+                       element, "style", "display: none; color: red;", NULL);
+
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-evo-draft", "", NULL);
+
+       document_element = webkit_dom_document_get_document_element (document);
+       content = webkit_dom_html_element_get_outer_html (
+               WEBKIT_DOM_HTML_ELEMENT (document_element));
+
+       webkit_dom_element_remove_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-evo-draft");
+
+       if (element)
+               webkit_dom_element_set_attribute (
+                       element, "style", "color: red;", NULL);
+
+       return content;
+}
+
+static gchar *
+process_content_for_mode_change (EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *body;
+       GString *plain_text;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document));
+
+       plain_text = g_string_sized_new (1024);
+
+       process_elements (view, body, FALSE, TRUE, TRUE, plain_text);
+
+       g_string_append (plain_text, "</body></html>");
+
+       return g_string_free (plain_text, FALSE);
+}
+
+static void
+convert_element_from_html_to_plain_text (EHTMLEditorView *view,
+                                         WebKitDOMElement *element,
+                                         gboolean *wrap,
+                                         gboolean *quote)
+{
+       EHTMLEditorSelection *selection;
+       gint blockquotes_count;
+       gchar *inner_text, *inner_html;
+       gboolean restore = TRUE;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *top_signature, *signature, *blockquote, *main_blockquote;
+       WebKitDOMNode *signature_clone, *from;
+
+       selection = e_html_editor_view_get_selection (view);
+
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
+
+       top_signature = webkit_dom_element_query_selector (
+               element, ".-x-evo-top-signature", NULL);
+       signature = webkit_dom_element_query_selector (
+               element, "span.-x-evo-signature", NULL);
+       main_blockquote = webkit_dom_element_query_selector (
+               element, "#-x-evo-main-cite", NULL);
+
+       blockquote = webkit_dom_document_create_element (
+               document, "blockquote", NULL);
+
+       if (main_blockquote) {
+               WebKitDOMElement *input_start;
+
+               webkit_dom_element_set_attribute (
+                       blockquote, "type", "cite", NULL);
+
+               input_start = webkit_dom_element_query_selector (
+                       element, "#-x-evo-input-start", NULL);
+
+               restore = input_start ? TRUE : FALSE;
+
+               if (input_start) {
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (input_start),
+                               e_html_editor_selection_get_caret_position_node (
+                                       document),
+                               NULL);
+               }
+               from = WEBKIT_DOM_NODE (main_blockquote);
+       } else {
+               if (signature) {
+                       signature_clone = webkit_dom_node_clone_node (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (signature)),
+                               TRUE);
+
+                       webkit_dom_node_remove_child (
+                               WEBKIT_DOM_NODE (element),
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (signature)),
+                               NULL);
+               }
+               from = WEBKIT_DOM_NODE (element);
+       }
+
+       blockquotes_count = create_text_markers_for_citations_in_element (
+               WEBKIT_DOM_ELEMENT (from));
+
+       inner_text = webkit_dom_html_element_get_inner_text (
+               WEBKIT_DOM_HTML_ELEMENT (from));
+
+       webkit_dom_html_element_set_inner_text (
+               WEBKIT_DOM_HTML_ELEMENT (blockquote), inner_text, NULL);
+
+       inner_html = webkit_dom_html_element_get_inner_html (
+               WEBKIT_DOM_HTML_ELEMENT (blockquote));
+
+       parse_html_into_paragraphs (
+               view, document,
+               main_blockquote ? blockquote : WEBKIT_DOM_ELEMENT (element),
+               inner_html,
+               FALSE);
+
+       if (main_blockquote) {
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (
+                               WEBKIT_DOM_NODE (main_blockquote)),
+                       WEBKIT_DOM_NODE (blockquote),
+                       WEBKIT_DOM_NODE (main_blockquote),
+                       NULL);
+
+               remove_evolution_attributes (WEBKIT_DOM_ELEMENT (element));
+               *wrap = TRUE;
+       } else {
+               WebKitDOMNode *first_child;
+
+               if (signature) {
+                       if (!top_signature) {
+                               signature_clone = webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (element),
+                                       signature_clone,
+                                       NULL);
+                       } else {
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (element),
+                                       signature_clone,
+                                       webkit_dom_node_get_first_child (
+                                               WEBKIT_DOM_NODE (element)),
+                                       NULL);
+                       }
+               }
+
+               first_child = webkit_dom_node_get_first_child (
+                       WEBKIT_DOM_NODE (element));
+
+               if (!webkit_dom_node_has_child_nodes (first_child)) {
+                       webkit_dom_html_element_set_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (first_child),
+                               UNICODE_ZERO_WIDTH_SPACE,
+                               NULL);
+               }
+               webkit_dom_node_insert_before (
+                       first_child,
+                       e_html_editor_selection_get_caret_position_node (
+                               document),
+                       webkit_dom_node_get_first_child (first_child),
+                       NULL);
+
+               *wrap = TRUE;
+       }
+
+       *quote = main_blockquote || blockquotes_count > 0;
+
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (element), "data-converted", "", NULL);
+
+       g_free (inner_text);
+       g_free (inner_html);
+
+       if (restore)
+               e_html_editor_selection_restore_caret_position (selection);
+}
+
+static gchar *
+process_content_for_plain_text (EHTMLEditorView *view)
+{
+       gboolean converted, wrap = FALSE, quote = FALSE, clean = FALSE;
+       gint length, ii;
+       GString *plain_text;
+       WebKitDOMDocument *document;
+       WebKitDOMNode *body, *source;
+       WebKitDOMNodeList *paragraphs;
+
+       plain_text = g_string_sized_new (1024);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document));
+       converted = webkit_dom_element_has_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-converted");
+       source = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), TRUE);
+
+       /* If composer is in HTML mode we have to move the content to plain version */
+       if (view->priv->html_mode) {
+               if (converted) {
+                       toggle_paragraphs_style_in_element (
+                               view, WEBKIT_DOM_ELEMENT (source), FALSE);
+                       remove_images_in_element (
+                               view, WEBKIT_DOM_ELEMENT (source), FALSE);
+               } else {
+                       gchar *inner_html;
+                       WebKitDOMElement *div;
+
+                       inner_html = webkit_dom_html_element_get_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (body));
+
+                       div = webkit_dom_document_create_element (
+                               document, "div", NULL);
+
+                       webkit_dom_html_element_set_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (div), inner_html, NULL);
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (body),
+                               WEBKIT_DOM_NODE (div),
+                               NULL);
+
+                       paragraphs = webkit_dom_element_query_selector_all (
+                               div, "#-x-evo-input-start", NULL);
+
+                       length = webkit_dom_node_list_get_length (paragraphs);
+                       for (ii = 0; ii < length; ii++) {
+                               WebKitDOMNode *paragraph;
+
+                               paragraph = webkit_dom_node_list_item (paragraphs, ii);
+
+                               webkit_dom_element_set_id (
+                                       WEBKIT_DOM_ELEMENT (paragraph), "");
+                       }
+
+                       convert_element_from_html_to_plain_text (
+                               view, div, &wrap, &quote);
+
+                       source = WEBKIT_DOM_NODE (div);
+
+                       clean = TRUE;
+               }
+       }
+
+       paragraphs = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (source), ".-x-evo-paragraph", NULL);
+
+       length = webkit_dom_node_list_get_length (paragraphs);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *paragraph;
+
+               paragraph = webkit_dom_node_list_item (paragraphs, ii);
+
+               if (WEBKIT_DOM_IS_HTMLO_LIST_ELEMENT (paragraph) ||
+                   WEBKIT_DOM_IS_HTMLU_LIST_ELEMENT (paragraph)) {
+                       WebKitDOMNode *item = webkit_dom_node_get_first_child (paragraph);
+
+                       while (item) {
+                               WebKitDOMNode *next_item =
+                                       webkit_dom_node_get_next_sibling (item);
+
+                               if (WEBKIT_DOM_IS_HTMLLI_ELEMENT (item)) {
+                                       e_html_editor_selection_wrap_paragraph (
+                                               e_html_editor_view_get_selection (view),
+                                               WEBKIT_DOM_ELEMENT (item));
+                               }
+                               item = next_item;
+                       }
+               } else {
+                       e_html_editor_selection_wrap_paragraph (
+                               e_html_editor_view_get_selection (view),
+                               WEBKIT_DOM_ELEMENT (paragraph));
+               }
+       }
+
+       paragraphs = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (source),
+               "span[id^=\"-x-evo-selection-\"], span#-x-evo-caret-position",
+               NULL);
+
+       length = webkit_dom_node_list_get_length (paragraphs);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (paragraphs, ii);
+               WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+
+               webkit_dom_node_remove_child (parent, node, NULL);
+               webkit_dom_node_normalize (parent);
+       }
+
+       if (view->priv->html_mode || quote)
+               quote_plain_text_recursive (document, source, source, 0);
+
+       process_elements (view, source, FALSE, FALSE, TRUE, plain_text);
+
+       if (clean)
+               webkit_dom_node_remove_child (
+                       WEBKIT_DOM_NODE (body), source, NULL);
+
+       /* Return text content between <body> and </body> */
+       return g_string_free (plain_text, FALSE);
+}
+
+static gchar *
+process_content_for_html (EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *body;
+       WebKitDOMElement *element;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document));
+       process_elements (view, body, TRUE, FALSE, FALSE, NULL);
+       element = webkit_dom_document_get_document_element (document);
+       return webkit_dom_html_element_get_outer_html (
+               WEBKIT_DOM_HTML_ELEMENT (element));
+}
+
+static gboolean
+show_lose_formatting_dialog (EHTMLEditorView *view)
+{
+       gint result;
+       GtkWidget *toplevel, *dialog;
+       GtkWindow *parent = NULL;
+
+       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
+
+       if (GTK_IS_WINDOW (toplevel))
+               parent = GTK_WINDOW (toplevel);
+
+       dialog = gtk_message_dialog_new (
+               parent,
+               GTK_DIALOG_DESTROY_WITH_PARENT,
+               GTK_MESSAGE_WARNING,
+               GTK_BUTTONS_NONE,
+               _("Turning HTML mode off will cause the text "
+               "to lose all formatting. Do you want to continue?"));
+       gtk_dialog_add_buttons (
+               GTK_DIALOG (dialog),
+               _("_Don't lose formatting"), GTK_RESPONSE_CANCEL,
+               _("_Lose formatting"), GTK_RESPONSE_OK,
+               NULL);
+
+       result = gtk_dialog_run (GTK_DIALOG (dialog));
+
+       if (result != GTK_RESPONSE_OK) {
+               gtk_widget_destroy (dialog);
+               /* Nothing has changed, but notify anyway */
+               g_object_notify (G_OBJECT (view), "html-mode");
+               return FALSE;
+       }
+
+       gtk_widget_destroy (dialog);
+
+       return TRUE;
+}
+
+static void
+clear_attributes (WebKitDOMDocument *document)
+{
+       gint length, ii;
+       WebKitDOMNamedNodeMap *attributes;
+       WebKitDOMHTMLElement *body = webkit_dom_document_get_body (document);
+       WebKitDOMHTMLHeadElement *head = webkit_dom_document_get_head (document);
+       WebKitDOMElement *document_element =
+               webkit_dom_document_get_document_element (document);
+
+       /* Remove all attributes from HTML element */
+       attributes = webkit_dom_element_get_attributes (document_element);
+       length = webkit_dom_named_node_map_get_length (attributes);
+       for (ii = length - 1; ii >= 0; ii--) {
+               WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+               webkit_dom_element_remove_attribute_node (
+                       document_element, WEBKIT_DOM_ATTR (node), NULL);
+       }
+
+       /* Remove everything from HEAD element */
+       while (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (head))) {
+               webkit_dom_node_remove_child (
+                       WEBKIT_DOM_NODE (head),
+                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (head)),
+                       NULL);
+       }
+
+       /* Remove non Evolution attributes from BODY element */
+       attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+       length = webkit_dom_named_node_map_get_length (attributes);
+       for (ii = length - 1; ii >= 0; ii--) {
+               gchar *name;
+               WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+               name = webkit_dom_node_get_local_name (node);
+
+               if (!g_str_has_prefix (name, "data-") ||
+                   g_str_has_prefix (name, "data-inline") ||
+                   g_str_has_prefix (name, "data-name")) {
+                       webkit_dom_element_remove_attribute_node (
+                               WEBKIT_DOM_ELEMENT (body),
+                               WEBKIT_DOM_ATTR (node),
+                               NULL);
+               }
+
+               g_free (name);
+       }
+}
+
+static void
+convert_when_changing_composer_mode (EHTMLEditorView *view)
+{
+       EHTMLEditorSelection *selection;
+       gboolean quote = FALSE, wrap = FALSE;
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+
+       selection = e_html_editor_view_get_selection (view);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+
+       convert_element_from_html_to_plain_text (
+               view, WEBKIT_DOM_ELEMENT (body), &wrap, &quote);
+
+       if (wrap)
+               e_html_editor_selection_wrap_paragraphs_in_document (selection, document);
+
+       if (quote) {
+               e_html_editor_selection_save_caret_position (selection);
+               body = WEBKIT_DOM_HTML_ELEMENT (e_html_editor_view_quote_plain_text (view));
+               e_html_editor_selection_restore_caret_position (selection);
+       }
+
+       clear_attributes (document);
+
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-converted", "", NULL);
+
+       /* Update fonts - in plain text we only want monospace */
+       e_html_editor_view_update_fonts (view);
+
+       e_html_editor_view_force_spell_check (view);
+}
+
+/**
+ * e_html_editor_view_set_html_mode:
+ * @view: an #EHTMLEditorView
+ * @html_mode: @TRUE to enable HTML mode, @FALSE to enable plain text mode
+ *
+ * When switching from HTML to plain text mode, user will be prompted whether
+ * he/she really wants to switch the mode and lose all formatting. When user
+ * declines, the property is not changed. When they accept, the all formatting
+ * is lost.
+ */
+void
+e_html_editor_view_set_html_mode (EHTMLEditorView *view,
+                                  gboolean html_mode)
+{
+       EHTMLEditorSelection *selection;
+       gboolean is_from_new_message, converted, edit_as_new, message, convert;
+       gboolean reply, hide;
+       WebKitDOMElement *blockquote;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMDocument *document;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+       selection = e_html_editor_view_get_selection (view);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+
+       is_from_new_message = webkit_dom_element_has_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-new-message");
+       converted = webkit_dom_element_has_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-converted");
+       edit_as_new = webkit_dom_element_has_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-edit-as-new");
+       message = webkit_dom_element_has_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-message");
+
+       reply = !is_from_new_message && !edit_as_new && message;
+       hide = !reply && !converted;
+
+       convert = message && ((!hide && reply && !converted) || (edit_as_new && !converted));
+
+       /* If toggling from HTML to plain text mode, ask user first */
+       if (convert && view->priv->html_mode && !html_mode) {
+               if (!show_lose_formatting_dialog (view))
+                       return;
+
+               view->priv->html_mode = html_mode;
+
+               convert_when_changing_composer_mode (view);
+
+               goto out;
+       }
+
+       if (html_mode == view->priv->html_mode)
+               return;
+
+       view->priv->html_mode = html_mode;
+
+       /* Update fonts - in plain text we only want monospace */
+       e_html_editor_view_update_fonts (view);
+
+       blockquote = webkit_dom_document_query_selector (
+               document, "blockquote[type|=cite]", NULL);
+
+       if (view->priv->html_mode) {
+               if (blockquote)
+                       e_html_editor_view_dequote_plain_text (view);
+
+               toggle_paragraphs_style (view);
+               toggle_smileys (view);
+               remove_wrapping (view);
+       } else {
+               gchar *plain;
+
+               /* Save caret position -> it will be restored in e-composer-private.c */
+               e_html_editor_selection_save_caret_position (selection);
+
+               if (blockquote) {
+                       e_html_editor_selection_wrap_paragraphs_in_document (
+                               selection, document);
+                       e_html_editor_view_quote_plain_text (view);
+               }
+
+               toggle_paragraphs_style (view);
+               toggle_smileys (view);
+               remove_images (view);
+
+               plain = process_content_for_mode_change (view);
+
+               if (*plain)
+                       webkit_web_view_load_string (
+                               WEBKIT_WEB_VIEW (view), plain, NULL, NULL, "file://");
+
+               g_free (plain);
+       }
+
+ out:
+       g_object_notify (G_OBJECT (view), "html-mode");
+}
+
+/**
+ * e_html_editor_view_get_inline_spelling:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns whether automatic spellchecking is enabled or not. When enabled,
+ * editor will perform spellchecking as user is typing. Otherwise spellcheck
+ * has to be run manually from menu.
+ *
+ * Returns: @TRUE when automatic spellchecking is enabled, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_get_inline_spelling (EHTMLEditorView *view)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+       return view->priv->inline_spelling;
+}
+
+/**
+ * e_html_editor_view_set_inline_spelling:
+ * @view: an #EHTMLEditorView
+ * @inline_spelling: @TRUE to enable automatic spellchecking, @FALSE otherwise
+ *
+ * Enables or disables automatic spellchecking.
+ */
+void
+e_html_editor_view_set_inline_spelling (EHTMLEditorView *view,
+                                        gboolean inline_spelling)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+       if (view->priv->inline_spelling == inline_spelling)
+               return;
+
+       view->priv->inline_spelling = inline_spelling;
+
+       g_object_notify (G_OBJECT (view), "inline-spelling");
+}
+
+/**
+ * e_html_editor_view_get_magic_links:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns whether automatic links conversion is enabled. When enabled, the editor
+ * will automatically convert any HTTP links into clickable HTML links.
+ *
+ * Returns: @TRUE when magic links are enabled, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_get_magic_links (EHTMLEditorView *view)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+       return view->priv->magic_links;
+}
+
+/**
+ * e_html_editor_view_set_magic_links:
+ * @view: an #EHTMLEditorView
+ * @magic_links: @TRUE to enable magic links, @FALSE to disable them
+ *
+ * Enables or disables automatic links conversion.
+ */
+void
+e_html_editor_view_set_magic_links (EHTMLEditorView *view,
+                                    gboolean magic_links)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+       if (view->priv->magic_links == magic_links)
+               return;
+
+       view->priv->magic_links = magic_links;
+
+       g_object_notify (G_OBJECT (view), "magic-links");
+}
+
+/**
+ * e_html_editor_view_get_magic_smileys:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns whether automatic conversion of smileys is enabled or disabled. When
+ * enabled, the editor will automatically convert text smileys ( :-), ;-),...)
+ * into images.
+ *
+ * Returns: @TRUE when magic smileys are enabled, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_view_get_magic_smileys (EHTMLEditorView *view)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+       return view->priv->magic_smileys;
+}
+
+/**
+ * e_html_editor_view_set_magic_smileys:
+ * @view: an #EHTMLEditorView
+ * @magic_smileys: @TRUE to enable magic smileys, @FALSE to disable them
+ *
+ * Enables or disables magic smileys.
+ */
+void
+e_html_editor_view_set_magic_smileys (EHTMLEditorView *view,
+                                      gboolean magic_smileys)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+       if (view->priv->magic_smileys == magic_smileys)
+               return;
+
+       view->priv->magic_smileys = magic_smileys;
+
+       g_object_notify (G_OBJECT (view), "magic-smileys");
+}
+
+/**
+ * e_html_editor_view_get_spell_checker:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns an #ESpellChecker object that is used to perform spellchecking.
+ *
+ * Returns: An always-valid #ESpellChecker object
+ */
+ESpellChecker *
+e_html_editor_view_get_spell_checker (EHTMLEditorView *view)
+{
+       return E_SPELL_CHECKER (webkit_get_text_checker ());
+}
+
+/**
+ * e_html_editor_view_get_text_html:
+ * @view: an #EHTMLEditorView:
+ *
+ * Returns processed HTML content of the editor document (with elements attributes
+ * used in Evolution composer)
+ *
+ * Returns: A newly allocated string
+ */
+gchar *
+e_html_editor_view_get_text_html (EHTMLEditorView *view)
+{
+       return process_content_for_html (view);
+}
+
+/**
+ * e_html_editor_view_get_text_html_for_drafts:
+ * @view: an #EHTMLEditorView:
+ *
+ * Returns HTML content of the editor document (without elements attributes
+ * used in Evolution composer)
+ *
+ * Returns: A newly allocated string
+ */
+gchar *
+e_html_editor_view_get_text_html_for_drafts (EHTMLEditorView *view)
+{
+       return process_content_for_saving_as_draft (view);
+}
+
+/**
+ * e_html_editor_view_get_text_plain:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns plain text content of the @view. The algorithm removes any
+ * formatting or styles from the document and keeps only the text and line
+ * breaks.
+ *
+ * Returns: A newly allocated string with plain text content of the document.
+ */
+gchar *
+e_html_editor_view_get_text_plain (EHTMLEditorView *view)
+{
+       return process_content_for_plain_text (view);
+}
+
+static void
+convert_and_load_html_to_plain_text (EHTMLEditorView *view,
+                                     const gchar *html)
+{
+       view->priv->convertor_insert = FALSE;
+
+       webkit_web_view_load_string (
+               view->priv->convertor_web_view, html, NULL, NULL, "file://");
+}
+
+void
+e_html_editor_view_convert_and_insert_html_to_plain_text (EHTMLEditorView *view,
+                                                          const gchar *html)
+{
+       view->priv->convertor_insert = TRUE;
+
+       webkit_web_view_load_string (
+               view->priv->convertor_web_view, html, NULL, NULL, "file://");
+}
+
+/**
+ * e_html_editor_view_set_text_html:
+ * @view: an #EHTMLEditorView
+ * @text: HTML code to load into the editor
+ *
+ * Loads given @text into the editor, destroying any content already present.
+ */
+void
+e_html_editor_view_set_text_html (EHTMLEditorView *view,
+                                  const gchar *text)
+{
+       view->priv->reload_in_progress = TRUE;
+
+       /* Only convert messages that are in HTML */
+       if (!view->priv->html_mode && *text && !strstr (text, "data-evo-draft")) {
+               if (strstr (text, "<!-- text/html -->")) {
+                       if (!show_lose_formatting_dialog (view)) {
+                               e_html_editor_view_set_html_mode (view, TRUE);
+                               webkit_web_view_load_string (
+                                       WEBKIT_WEB_VIEW (view), text, NULL, NULL, "file://");
+                               return;
+                       }
+                       convert_and_load_html_to_plain_text (view, text);
+               } else {
+                       convert_and_load_html_to_plain_text (view, text);
+               }
+       } else {
+               webkit_web_view_load_string (
+                       WEBKIT_WEB_VIEW (view), text, NULL, NULL, "file://");
+       }
+}
+
+/**
+ * e_html_editor_view_set_text_plain:
+ * @view: an #EHTMLEditorView
+ * @text: A plain text to load into the editor
+ *
+ * Loads given @text into the editor, destryoing any content already present.
+ */
+void
+e_html_editor_view_set_text_plain (EHTMLEditorView *view,
+                                   const gchar *text)
+{
+       view->priv->reload_in_progress = TRUE;
+
+       webkit_web_view_load_string (
+               WEBKIT_WEB_VIEW (view), text, "text/plain", NULL, "file://");
+}
+
+/**
+ * e_html_editor_view_paste_clipboard_quoted:
+ * @view: an #EHTMLEditorView
+ *
+ * Pastes current content of clipboard into the editor as quoted text
+ */
+void
+e_html_editor_view_paste_clipboard_quoted (EHTMLEditorView *view)
+{
+       EHTMLEditorViewClass *class;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+       class = E_HTML_EDITOR_VIEW_GET_CLASS (view);
+       g_return_if_fail (class->paste_clipboard_quoted != NULL);
+
+       class->paste_clipboard_quoted (view);
+}
+
+void
+e_html_editor_view_embed_styles (EHTMLEditorView *view)
+{
+       WebKitWebSettings *settings;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *sheet;
+       gchar *stylesheet_uri;
+       gchar *stylesheet_content;
+       const gchar *stylesheet;
+       gsize length;
+
+       settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       g_object_get (
+               G_OBJECT (settings),
+               "user-stylesheet-uri", &stylesheet_uri,
+               NULL);
+
+       stylesheet = strstr (stylesheet_uri, ",");
+       stylesheet_content = (gchar *) g_base64_decode (stylesheet, &length);
+       g_free (stylesheet_uri);
+
+       if (length == 0) {
+               g_free (stylesheet_content);
+               return;
+       }
+
+       e_web_view_create_and_add_css_style_sheet (document, "-x-evo-composer-sheet");
+
+       sheet = webkit_dom_document_get_element_by_id (document, "-x-evo-composer-sheet");
+       webkit_dom_element_set_attribute (
+               sheet,
+               "type",
+               "text/css",
+               NULL);
+
+       webkit_dom_html_element_set_inner_html (WEBKIT_DOM_HTML_ELEMENT (sheet), stylesheet_content, NULL);
+
+       g_free (stylesheet_content);
+}
+
+void
+e_html_editor_view_remove_embed_styles (EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *sheet;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       sheet = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-composer-sheet");
+
+       webkit_dom_node_remove_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (sheet)),
+               WEBKIT_DOM_NODE (sheet),
+               NULL);
+}
+
+static const gchar *
+citation_color_level_1 (void)
+{
+       return "rgb(114,159,207)";  /* Sky Blue 1 */
+}
+
+static const gchar *
+citation_color_level_2 (void)
+{
+       return "rgb(173,127,168)";  /* Plum 1 */
+}
+
+static const gchar *
+citation_color_level_3 (void)
+{
+       return "rgb(138,226,52)";  /* Chameleon 1 */
+}
+
+static const gchar *
+citation_color_level_4 (void)
+{
+       return "rgb(252,175,62)";  /* Orange 1 */
+}
+
+static const gchar *
+citation_color_level_5 (void)
+{
+       return "rgb(233,185,110)";  /* Chocolate 1 */
+}
+
+/**
+ * e_html_editor_view_update_fonts:
+ * @view: an #EHTMLEditorView
+ *
+ * Forces the editor to reload font settings from WebKitWebSettings and apply
+ * it on the content of the editor document.
+ */
+void
+e_html_editor_view_update_fonts (EHTMLEditorView *view)
+{
+       GString *stylesheet;
+       gchar *base64;
+       gchar *aa = NULL;
+       WebKitWebSettings *settings;
+       PangoFontDescription *ms, *vw;
+       const gchar *styles[] = { "normal", "oblique", "italic" };
+       const gchar *smoothing = NULL;
+       GtkStyleContext *context;
+       GdkColor *link = NULL;
+       GdkColor *visited = NULL;
+       gchar *font;
+
+       font = g_settings_get_string (
+               view->priv->font_settings,
+               "monospace-font-name");
+       ms = pango_font_description_from_string (
+               font ? font : "monospace 10");
+       g_free (font);
+
+       if (view->priv->html_mode) {
+               font = g_settings_get_string (
+                               view->priv->font_settings,
+                               "font-name");
+               vw = pango_font_description_from_string (
+                               font ? font : "serif 10");
+               g_free (font);
+       } else {
+               /* When in plain text mode, force monospace font */
+               vw = pango_font_description_copy (ms);
+       }
+
+       stylesheet = g_string_new ("");
+       g_string_append_printf (
+               stylesheet,
+               "body {\n"
+               "  font-family: '%s';\n"
+               "  font-size: %dpt;\n"
+               "  font-weight: %d;\n"
+               "  font-style: %s;\n",
+               pango_font_description_get_family (vw),
+               pango_font_description_get_size (vw) / PANGO_SCALE,
+               pango_font_description_get_weight (vw),
+               styles[pango_font_description_get_style (vw)]);
+
+       if (view->priv->aliasing_settings != NULL)
+               aa = g_settings_get_string (
+                       view->priv->aliasing_settings, "antialiasing");
+
+       if (g_strcmp0 (aa, "none") == 0)
+               smoothing = "none";
+       else if (g_strcmp0 (aa, "grayscale") == 0)
+               smoothing = "antialiased";
+       else if (g_strcmp0 (aa, "rgba") == 0)
+               smoothing = "subpixel-antialiased";
+
+       if (smoothing != NULL)
+               g_string_append_printf (
+                       stylesheet,
+                       " -webkit-font-smoothing: %s;\n",
+                       smoothing);
+
+       g_free (aa);
+
+       g_string_append (stylesheet, "}\n");
+
+       g_string_append_printf (
+               stylesheet,
+               "pre,code,.pre {\n"
+               "  font-family: '%s';\n"
+               "  font-size: %dpt;\n"
+               "  font-weight: %d;\n"
+               "  font-style: %s;\n"
+               "}",
+               pango_font_description_get_family (ms),
+               pango_font_description_get_size (ms) / PANGO_SCALE,
+               pango_font_description_get_weight (ms),
+               styles[pango_font_description_get_style (ms)]);
+
+       context = gtk_widget_get_style_context (GTK_WIDGET (view));
+       gtk_style_context_get_style (
+               context,
+               "link-color", &link,
+               "visited-link-color", &visited,
+               NULL);
+
+       if (link == NULL) {
+               link = g_slice_new0 (GdkColor);
+               link->blue = G_MAXINT16;
+       }
+
+       if (visited == NULL) {
+               visited = g_slice_new0 (GdkColor);
+               visited->red = G_MAXINT16;
+       }
+
+       g_string_append_printf (
+               stylesheet,
+               "a {\n"
+               "  color: #%06x;\n"
+               "}\n"
+               "a:visited {\n"
+               "  color: #%06x;\n"
+               "}\n",
+               e_color_to_value (link),
+               e_color_to_value (visited));
+
+       /* See bug #689777 for details */
+       g_string_append (
+               stylesheet,
+               "p,pre,code,address {\n"
+               "  margin: 0;\n"
+               "}\n"
+               "h1,h2,h3,h4,h5,h6 {\n"
+               "  margin-top: 0.2em;\n"
+               "  margin-bottom: 0.2em;\n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "img "
+               "{\n"
+               "  height: inherit; \n"
+               "  width: inherit; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "span.-x-evo-resizable-wrapper "
+               "{\n"
+               "  resize: both; \n"
+               "  overflow: hidden; \n"
+               "  display: inline-block; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "span.-x-evo-resizable-wrapper:hover "
+               "{\n"
+               "  outline: 1px dashed red; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "ul,ol "
+               "{\n"
+               "  -webkit-padding-start: 7ch; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               ".-x-evo-list-item-alignt-left "
+               "{\n"
+               "  text-align: left; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               ".-x-evo-list-item-align-center "
+               "{\n"
+               "  text-align: center; \n"
+               "  -webkit-padding-start: 0ch; \n"
+               "  margin-left: -3ch; \n"
+               "  margin-right: 1ch; \n"
+               "  list-style-position: inside; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               ".-x-evo-list-item-align-right "
+               "{\n"
+               "  text-align: right; \n"
+               "  -webkit-padding-start: 0ch; \n"
+               "  margin-left: -3ch; \n"
+               "  margin-right: 1ch; \n"
+               "  list-style-position: inside; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "ol,ul "
+               "{\n"
+               "  -webkit-margin-before: 0em; \n"
+               "  -webkit-margin-after: 0em; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "blockquote "
+               "{\n"
+               "  -webkit-margin-before: 0em; \n"
+               "  -webkit-margin-after: 0em; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "blockquote[type=cite] "
+               "{\n"
+               "  padding: 0.0ex 0ex;\n"
+               "  margin: 0ex;\n"
+               "  -webkit-margin-start: 0em; \n"
+               "  -webkit-margin-end : 0em; \n"
+               "  color: #737373 !important;\n"
+               "}\n");
+
+       g_string_append_printf (
+               stylesheet,
+               ".quote_character "
+               "{\n"
+               "  color: %s;\n"
+               "}\n",
+               citation_color_level_1 ());
+
+       g_string_append_printf (
+               stylesheet,
+               ".quote_character+"
+               ".quote_character"
+               "{\n"
+               "  color: %s;\n"
+               "}\n",
+               citation_color_level_2 ());
+
+       g_string_append_printf (
+               stylesheet,
+               ".quote_character+"
+               ".quote_character+"
+               ".quote_character"
+               "{\n"
+               "  color: %s;\n"
+               "}\n",
+               citation_color_level_3 ());
+
+       g_string_append_printf (
+               stylesheet,
+               ".quote_character+"
+               ".quote_character+"
+               ".quote_character+"
+               ".quote_character"
+               "{\n"
+               "  color: %s;\n"
+               "}\n",
+               citation_color_level_4 ());
+
+       g_string_append_printf (
+               stylesheet,
+               ".quote_character+"
+               ".quote_character+"
+               ".quote_character+"
+               ".quote_character+"
+               ".quote_character"
+               "{\n"
+               "  color: %s;\n"
+               "}\n",
+               citation_color_level_5 ());
+
+       g_string_append (
+               stylesheet,
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "{\n"
+               "  padding: 0.4ex 1ex;\n"
+               "  margin: 1ex;\n"
+               "  border-width: 0px 2px 0px 2px;\n"
+               "  border-style: none solid none solid;\n"
+               "  border-radius: 2px;\n"
+               "}\n");
+
+       /* Block quote border colors are borrowed from Thunderbird. */
+
+       g_string_append_printf (
+               stylesheet,
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "{\n"
+               "  border-color: %s;\n"
+               "}\n",
+               citation_color_level_1 ());
+
+       g_string_append_printf (
+               stylesheet,
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "{\n"
+               "  border-color: %s;\n"
+               "}\n",
+               citation_color_level_2 ());
+
+       g_string_append_printf (
+               stylesheet,
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "{\n"
+               "  border-color: %s;\n"
+               "}\n",
+               citation_color_level_3 ());
+
+       g_string_append_printf (
+               stylesheet,
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "{\n"
+               "  border-color: %s;\n"
+               "}\n",
+               citation_color_level_4 ());
+
+       g_string_append_printf (
+               stylesheet,
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "{\n"
+               "  border-color: %s;\n"
+               "}\n",
+               citation_color_level_5 ());
+
+       gdk_color_free (link);
+       gdk_color_free (visited);
+
+       base64 = g_base64_encode ((guchar *) stylesheet->str, stylesheet->len);
+       g_string_free (stylesheet, TRUE);
+
+       stylesheet = g_string_new ("data:text/css;charset=utf-8;base64,");
+       g_string_append (stylesheet, base64);
+       g_free (base64);
+
+       settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
+       g_object_set (
+               G_OBJECT (settings),
+               "default-font-size", pango_font_description_get_size (vw) / PANGO_SCALE,
+               "default-font-family", pango_font_description_get_family (vw),
+               "monospace-font-family", pango_font_description_get_family (ms),
+               "default-monospace-font-size", (pango_font_description_get_size (ms) / PANGO_SCALE),
+               "user-stylesheet-uri", stylesheet->str,
+               NULL);
+
+       g_string_free (stylesheet, TRUE);
+
+       pango_font_description_free (ms);
+       pango_font_description_free (vw);
+}
+
+/**
+ * e_html_editor_view_get_element_under_mouse_click:
+ * @view: an #EHTMLEditorView
+ *
+ * Returns DOM element, that was clicked on.
+ *
+ * Returns: DOM element on that was clicked.
+ */
+WebKitDOMElement *
+e_html_editor_view_get_element_under_mouse_click (EHTMLEditorView *view)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL);
+
+       return view->priv->element_under_mouse;
+}
+
+/**
+ * e_html_editor_view_check_magic_links
+ * @view: an #EHTMLEditorView
+ * @include_space: If TRUE the pattern for link expects space on end
+ *
+ * Check if actual selection in given editor is link. If so, it is surrounded
+ * with ANCHOR element.
+ */
+void
+e_html_editor_view_check_magic_links (EHTMLEditorView *view,
+                                      gboolean include_space)
+{
+       WebKitDOMRange *range;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+       range = html_editor_view_get_dom_range (view);
+       html_editor_view_check_magic_links (view, range, include_space, NULL);
+}
+
+static CamelMimePart *
+e_html_editor_view_add_inline_image_from_element (EHTMLEditorView *view,
+                                                  WebKitDOMElement *element,
+                                                  const gchar *attribute)
+{
+       CamelStream *stream;
+       CamelDataWrapper *wrapper;
+       CamelMimePart *part = NULL;
+       gsize decoded_size;
+       gssize size;
+       gchar *mime_type = NULL;
+       gchar *element_src, *cid, *name;
+       const gchar *base64_encoded_data;
+       guchar *base64_decoded_data;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element)) {
+               return NULL;
+       }
+
+       element_src = webkit_dom_element_get_attribute (
+               WEBKIT_DOM_ELEMENT (element), attribute);
+
+       base64_encoded_data = strstr (element_src, ";base64,");
+       if (!base64_encoded_data)
+               goto out;
+
+       mime_type = g_strndup (
+               element_src + 5,
+               base64_encoded_data - (strstr (element_src, "data:") + 5));
+
+       /* Move to actual data */
+       base64_encoded_data += 8;
+
+       base64_decoded_data = g_base64_decode (base64_encoded_data, &decoded_size);
+
+       stream = camel_stream_mem_new ();
+       size = camel_stream_write (
+               stream, (gchar *) base64_decoded_data, decoded_size, NULL, NULL);
+
+       if (size == -1)
+               goto out;
+
+       wrapper = camel_data_wrapper_new ();
+       camel_data_wrapper_construct_from_stream_sync (
+               wrapper, stream, NULL, NULL);
+       g_object_unref (stream);
+
+       camel_data_wrapper_set_mime_type (wrapper, mime_type);
+
+       part = camel_mime_part_new ();
+       camel_medium_set_content (CAMEL_MEDIUM (part), wrapper);
+       g_object_unref (wrapper);
+
+       cid = camel_header_msgid_generate ();
+       camel_mime_part_set_content_id (part, cid);
+       name = webkit_dom_element_get_attribute (element, "data-name");
+       camel_mime_part_set_filename (part, name);
+       g_free (name);
+       camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
+out:
+       g_free (mime_type);
+       g_free (element_src);
+       g_free (base64_decoded_data);
+
+       return part;
+}
+
+GList *
+e_html_editor_view_get_parts_for_inline_images (EHTMLEditorView *view)
+{
+       GHashTable *added;
+       GList *parts = NULL;
+       gint length, ii;
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *list;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW  (view));
+       list = webkit_dom_document_query_selector_all (document, "img[data-inline]", NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+       if (length == 0)
+               return parts;
+
+       added = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+       for (ii = 0; ii < length; ii++) {
+               CamelMimePart *part;
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               gchar *src = webkit_dom_element_get_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "src");
+
+               if (!g_hash_table_lookup (added, src)) {
+                       part = e_html_editor_view_add_inline_image_from_element (
+                               view, WEBKIT_DOM_ELEMENT (node), "src");
+                       parts = g_list_append (parts, part);
+                       g_hash_table_insert (
+                               added, src, (gpointer) camel_mime_part_get_content_id (part));
+               }
+               g_free (src);
+       }
+
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               const gchar *id;
+               gchar *src = webkit_dom_element_get_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "src");
+
+               if ((id = g_hash_table_lookup (added, src)) != NULL) {
+                       gchar *cid = g_strdup_printf ("cid:%s", id);
+                       webkit_dom_element_set_attribute (
+                               WEBKIT_DOM_ELEMENT (node), "src", cid, NULL);
+                       g_free (cid);
+               }
+               g_free (src);
+       }
+
+       list = webkit_dom_document_query_selector_all (
+               document, "[data-inline][background]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               CamelMimePart *part;
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               gchar *src = webkit_dom_element_get_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "background");
+
+               if (!g_hash_table_lookup (added, src)) {
+                       part = e_html_editor_view_add_inline_image_from_element (
+                               view, WEBKIT_DOM_ELEMENT (node), "background");
+                       if (part) {
+                               parts = g_list_append (parts, part);
+                               g_hash_table_insert (
+                                       added, src,
+                                       (gpointer) camel_mime_part_get_content_id (part));
+                       }
+               }
+               g_free (src);
+       }
+
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               gchar *src = webkit_dom_element_get_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "background");
+               const gchar *id;
+
+               if ((id = g_hash_table_lookup (added, src)) != NULL) {
+                       gchar *cid = g_strdup_printf ("cid:%s", id);
+                       webkit_dom_element_set_attribute (
+                               WEBKIT_DOM_ELEMENT (node), "background", cid, NULL);
+                       g_free (cid);
+               }
+               g_free (src);
+       }
+
+       g_hash_table_destroy (added);
+
+       return parts;
+}
+
+/**
+ * e_html_editor_view_add_inline_image_from_mime_part:
+ * @composer: a composer object
+ * @part: a CamelMimePart containing image data
+ *
+ * This adds the mime part @part to @composer as an inline image.
+ **/
+void
+e_html_editor_view_add_inline_image_from_mime_part (EHTMLEditorView *view,
+                                                    CamelMimePart *part)
+{
+       CamelDataWrapper *dw;
+       CamelStream *stream;
+       GByteArray *byte_array;
+       gchar *src, *base64_encoded, *mime_type, *cid_src;
+       const gchar *cid, *name;
+
+       stream = camel_stream_mem_new ();
+       dw = camel_medium_get_content (CAMEL_MEDIUM (part));
+       g_return_if_fail (dw);
+
+       mime_type = camel_data_wrapper_get_mime_type (dw);
+       camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL);
+       camel_stream_close (stream, NULL, NULL);
+
+       byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
+
+       if (!byte_array->data)
+               return;
+
+       base64_encoded = g_base64_encode ((const guchar *) byte_array->data, byte_array->len);
+
+       name = camel_mime_part_get_filename (part);
+       /* Insert file name before new src */
+       src = g_strconcat (name, ";data:", mime_type, ";base64,", base64_encoded, NULL);
+
+       cid = camel_mime_part_get_content_id (part);
+       if (!cid) {
+               camel_mime_part_set_content_id (part, NULL);
+               cid = camel_mime_part_get_content_id (part);
+       }
+       cid_src = g_strdup_printf ("cid:%s", cid);
+
+       g_hash_table_insert (view->priv->inline_images, cid_src, src);
+
+       g_free (base64_encoded);
+       g_free (mime_type);
+       g_object_unref (stream);
+}
diff --git a/e-util/e-html-editor-view.h b/e-util/e-html-editor-view.h
new file mode 100644
index 0000000..e40b15b
--- /dev/null
+++ b/e-util/e-html-editor-view.h
@@ -0,0 +1,164 @@
+/*
+ * e-html-editor-view.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_VIEW_H
+#define E_HTML_EDITOR_VIEW_H
+
+#include <webkit/webkit.h>
+
+#include <camel/camel.h>
+
+#include <e-util/e-html-editor-selection.h>
+#include <e-util/e-emoticon.h>
+#include <e-util/e-spell-checker.h>
+#include <e-util/e-util-enums.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR_VIEW \
+       (e_html_editor_view_get_type ())
+#define E_HTML_EDITOR_VIEW(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorView))
+#define E_HTML_EDITOR_VIEW_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorViewClass))
+#define E_IS_HTML_EDITOR_VIEW(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_VIEW))
+#define E_IS_HTML_EDITOR_VIEW_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_VIEW))
+#define E_HTML_EDITOR_VIEW_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_VIEW, EHTMLEditorViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorView EHTMLEditorView;
+typedef struct _EHTMLEditorViewClass EHTMLEditorViewClass;
+typedef struct _EHTMLEditorViewPrivate EHTMLEditorViewPrivate;
+
+struct _EHTMLEditorView {
+       WebKitWebView parent;
+       EHTMLEditorViewPrivate *priv;
+};
+
+struct _EHTMLEditorViewClass {
+       WebKitWebViewClass parent_class;
+
+       void            (*paste_clipboard_quoted)
+                                               (EHTMLEditorView *view);
+       gboolean        (*popup_event)          (EHTMLEditorView *view,
+                                                GdkEventButton *event);
+       void            (*paste_primary_clipboard)
+                                               (EHTMLEditorView *view);
+};
+
+GType          e_html_editor_view_get_type     (void) G_GNUC_CONST;
+EHTMLEditorView *
+               e_html_editor_view_new          (void);
+EHTMLEditorSelection *
+               e_html_editor_view_get_selection
+                                               (EHTMLEditorView *view);
+gboolean       e_html_editor_view_exec_command (EHTMLEditorView *view,
+                                                EHTMLEditorViewCommand command,
+                                                const gchar *value);
+gboolean       e_html_editor_view_get_changed  (EHTMLEditorView *view);
+void           e_html_editor_view_set_changed  (EHTMLEditorView *view,
+                                                gboolean changed);
+gboolean       e_html_editor_view_get_html_mode
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_set_html_mode
+                                               (EHTMLEditorView *view,
+                                                gboolean html_mode);
+gboolean       e_html_editor_view_get_inline_spelling
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_set_inline_spelling
+                                               (EHTMLEditorView *view,
+                                                gboolean inline_spelling);
+gboolean       e_html_editor_view_get_magic_links
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_set_magic_links
+                                               (EHTMLEditorView *view,
+                                                gboolean magic_links);
+void           e_html_editor_view_insert_smiley
+                                               (EHTMLEditorView *view,
+                                                EEmoticon *emoticon);
+gboolean       e_html_editor_view_get_magic_smileys
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_set_magic_smileys
+                                               (EHTMLEditorView *view,
+                                                gboolean magic_smileys);
+ESpellChecker *        e_html_editor_view_get_spell_checker
+                                               (EHTMLEditorView *view);
+gchar *                e_html_editor_view_get_text_html
+                                               (EHTMLEditorView *view);
+gchar *                e_html_editor_view_get_text_html_for_drafts
+                                               (EHTMLEditorView *view);
+gchar *                e_html_editor_view_get_text_plain
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_convert_and_insert_html_to_plain_text
+                                               (EHTMLEditorView *view,
+                                                const gchar *html);
+void           e_html_editor_view_set_text_html
+                                               (EHTMLEditorView *view,
+                                                const gchar *text);
+void           e_html_editor_view_set_text_plain
+                                               (EHTMLEditorView *view,
+                                                const gchar *text);
+void           e_html_editor_view_paste_clipboard_quoted
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_embed_styles (EHTMLEditorView *view);
+void           e_html_editor_view_remove_embed_styles
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_update_fonts (EHTMLEditorView *view);
+WebKitDOMElement *
+               e_html_editor_view_get_element_under_mouse_click
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_check_magic_links
+                                               (EHTMLEditorView *view,
+                                                gboolean while_typing);
+WebKitDOMElement *
+               e_html_editor_view_quote_plain_text_element
+                                               (EHTMLEditorView *view,
+                                                 WebKitDOMElement *element);
+WebKitDOMElement *
+               e_html_editor_view_quote_plain_text
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_dequote_plain_text
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_turn_spell_check_off
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_force_spell_check_for_current_paragraph
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_force_spell_check
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_add_inline_image_from_mime_part
+                                               (EHTMLEditorView *view,
+                                                 CamelMimePart *part);
+GList *                e_html_editor_view_get_parts_for_inline_images
+                                               (EHTMLEditorView *view);
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_VIEW_H */
diff --git a/e-util/e-html-editor.c b/e-util/e-html-editor.c
new file mode 100644
index 0000000..aeae537
--- /dev/null
+++ b/e-util/e-html-editor.c
@@ -0,0 +1,1178 @@
+/*
+ * e-html-editor.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#include <camel/camel.h>
+#include <enchant/enchant.h>
+
+#include "e-html-editor.h"
+
+#include "e-activity-bar.h"
+#include "e-alert-bar.h"
+#include "e-alert-dialog.h"
+#include "e-alert-sink.h"
+#include "e-html-editor-private.h"
+#include "e-html-editor-utils.h"
+#include "e-html-editor-selection.h"
+
+#define E_HTML_EDITOR_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR, EHTMLEditorPrivate))
+
+/**
+ * EHTMLEditor:
+ *
+ * #EHTMLEditor provides GUI for manipulating with properties of #EHTMLEditorView and
+ * its #EHTMLEditorSelection - i.e. toolbars and actions.
+ */
+
+/* This controls how spelling suggestions are divided between the primary
+ * context menu and a secondary menu.  The idea is to prevent the primary
+ * menu from growing too long.
+ *
+ * The constants below are used as follows:
+ *
+ * if TOTAL_SUGGESTIONS <= MAX_LEVEL1_SUGGETIONS:
+ *     LEVEL1_SUGGESTIONS = TOTAL_SUGGESTIONS
+ * elif TOTAL_SUGGESTIONS - MAX_LEVEL1_SUGGESTIONS < MIN_LEVEL2_SUGGESTIONS:
+ *     LEVEL1_SUGGESTIONS = TOTAL_SUGGESTIONS
+ * else
+ *     LEVEL1_SUGGESTIONS = MAX_LEVEL1_SUGGETIONS
+ *
+ * LEVEL2_SUGGESTIONS = TOTAL_SUGGESTIONS - LEVEL1_SUGGESTIONS
+ *
+ * Note that MAX_LEVEL1_SUGGETIONS is not a hard maximum.
+ */
+#define MAX_LEVEL1_SUGGESTIONS 4
+#define MIN_LEVEL2_SUGGESTIONS 3
+
+enum {
+       PROP_0,
+       PROP_FILENAME
+};
+
+enum {
+       UPDATE_ACTIONS,
+       SPELL_LANGUAGES_CHANGED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/* Forward Declarations */
+static void    e_html_editor_alert_sink_init
+                                       (EAlertSinkInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+       EHTMLEditor,
+       e_html_editor,
+       GTK_TYPE_GRID,
+       G_IMPLEMENT_INTERFACE (
+               E_TYPE_ALERT_SINK,
+               e_html_editor_alert_sink_init))
+
+/* Action callback for context menu spelling suggestions.
+ * XXX This should really be in e-html-editor-actions.c */
+static void
+action_context_spell_suggest_cb (GtkAction *action,
+                                 EHTMLEditor *editor)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+       const gchar *word;
+
+       word = g_object_get_data (G_OBJECT (action), "word");
+       g_return_if_fail (word != NULL);
+
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       e_html_editor_selection_replace_caret_word (selection, word);
+}
+
+static void
+html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+       WebKitSpellChecker *checker;
+       GtkActionGroup *action_group;
+       GtkUIManager *manager;
+       gchar **suggestions;
+       const gchar *path;
+       gchar *word;
+       guint count = 0;
+       guint length;
+       guint merge_id;
+       guint threshold;
+       gint ii;
+
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+       checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+
+       word = e_html_editor_selection_get_caret_word (selection);
+       if (word == NULL || *word == '\0')
+               return;
+
+       suggestions = webkit_spell_checker_get_guesses_for_word (checker, word, NULL);
+
+       path = "/context-menu/context-spell-suggest/";
+       manager = e_html_editor_get_ui_manager (editor);
+       action_group = editor->priv->suggestion_actions;
+       merge_id = editor->priv->spell_suggestions_merge_id;
+
+       length = (suggestions != NULL) ? g_strv_length (suggestions) : 0;
+
+       /* Calculate how many suggestions to put directly in the
+        * context menu.  The rest will go in a secondary menu. */
+       if (length <= MAX_LEVEL1_SUGGESTIONS) {
+               threshold = length;
+       } else if (length - MAX_LEVEL1_SUGGESTIONS < MIN_LEVEL2_SUGGESTIONS) {
+               threshold = length;
+       } else {
+               threshold = MAX_LEVEL1_SUGGESTIONS;
+       }
+
+       ii = 0;
+       for (ii = 0; suggestions && suggestions[ii]; ii++) {
+               gchar *suggestion = suggestions[ii];
+               gchar *action_name;
+               gchar *action_label;
+               GtkAction *action;
+               GtkWidget *child;
+               GSList *proxies;
+
+               /* Once we reach the threshold, put all subsequent
+                * spelling suggestions in a secondary menu. */
+               if (count == threshold)
+                       path = "/context-menu/context-more-suggestions-menu/";
+
+               /* Action name just needs to be unique. */
+               action_name = g_strdup_printf ("suggest-%d", count++);
+
+               action_label = g_markup_printf_escaped (
+                       "<b>%s</b>", suggestion);
+
+               action = gtk_action_new (
+                       action_name, action_label, NULL, NULL);
+
+               g_object_set_data_full (
+                       G_OBJECT (action), "word",
+                       g_strdup (suggestion), g_free);
+
+               g_signal_connect (
+                       action, "activate", G_CALLBACK (
+                       action_context_spell_suggest_cb), editor);
+
+               gtk_action_group_add_action (action_group, action);
+
+               gtk_ui_manager_add_ui (
+                       manager, merge_id, path,
+                       action_name, action_name,
+                       GTK_UI_MANAGER_AUTO, FALSE);
+
+               /* XXX GtkAction offers no support for Pango markup,
+                *     so we have to manually set "use-markup" on the
+                *     child of the proxy widget. */
+               gtk_ui_manager_ensure_update (manager);
+               proxies = gtk_action_get_proxies (action);
+               child = gtk_bin_get_child (proxies->data);
+               g_object_set (child, "use-markup", TRUE, NULL);
+
+               g_free (action_name);
+               g_free (action_label);
+       }
+
+       g_free (word);
+       g_strfreev (suggestions);
+}
+
+/* Helper for html_editor_update_actions() */
+static void
+html_editor_spell_checkers_foreach (EHTMLEditor *editor,
+                               const gchar *language_code)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorSelection *selection;
+       ESpellChecker *spell_checker;
+       ESpellDictionary *dictionary;
+       GtkActionGroup *action_group;
+       GtkUIManager *manager;
+       GList *list, *link;
+       gchar *path;
+       gchar *word;
+       gint ii = 0;
+       guint merge_id;
+
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+       spell_checker = e_html_editor_view_get_spell_checker (view);
+
+       word = e_html_editor_selection_get_caret_word (selection);
+       if (word == NULL || *word == '\0')
+               return;
+
+       dictionary = e_spell_checker_ref_dictionary (
+               spell_checker, language_code);
+       if (dictionary != NULL) {
+               list = e_spell_dictionary_get_suggestions (
+                       dictionary, word, -1);
+               g_object_unref (dictionary);
+       } else {
+               list = NULL;
+       }
+
+       manager = e_html_editor_get_ui_manager (editor);
+       action_group = editor->priv->suggestion_actions;
+       merge_id = editor->priv->spell_suggestions_merge_id;
+
+       path = g_strdup_printf (
+               "/context-menu/context-spell-suggest/"
+               "context-spell-suggest-%s-menu", language_code);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               gchar *suggestion = link->data;
+               gchar *action_name;
+               gchar *action_label;
+               GtkAction *action;
+               GtkWidget *child;
+               GSList *proxies;
+
+               /* Action name just needs to be unique. */
+               action_name = g_strdup_printf (
+                       "suggest-%s-%d", language_code, ii);
+
+               action_label = g_markup_printf_escaped (
+                       "<b>%s</b>", suggestion);
+
+               action = gtk_action_new (
+                       action_name, action_label, NULL, NULL);
+
+               g_object_set_data_full (
+                       G_OBJECT (action), "word",
+                       g_strdup (suggestion), g_free);
+
+               g_signal_connect (
+                       action, "activate", G_CALLBACK (
+                       action_context_spell_suggest_cb), editor);
+
+               gtk_action_group_add_action (action_group, action);
+
+               gtk_ui_manager_add_ui (
+                       manager, merge_id, path,
+                       action_name, action_name,
+                       GTK_UI_MANAGER_AUTO, FALSE);
+
+               /* XXX GtkAction offers no supports for Pango markup,
+                *     so we have to manually set "use-markup" on the
+                *     child of the proxy widget. */
+               gtk_ui_manager_ensure_update (manager);
+               proxies = gtk_action_get_proxies (action);
+               if (proxies && proxies->data) {
+                       child = gtk_bin_get_child (proxies->data);
+                       g_object_set (child, "use-markup", TRUE, NULL);
+               }
+
+               g_free (action_name);
+               g_free (action_label);
+       }
+
+       g_list_free_full (list, (GDestroyNotify) g_free);
+
+       g_free (path);
+       g_free (word);
+}
+
+static void
+html_editor_update_actions (EHTMLEditor *editor,
+                       GdkEventButton *event)
+{
+       WebKitWebView *web_view;
+       WebKitSpellChecker *checker;
+       WebKitHitTestResult *hit_test;
+       WebKitHitTestResultContext context;
+       WebKitDOMNode *node;
+       EHTMLEditorSelection *selection;
+       EHTMLEditorView *view;
+       ESpellChecker *spell_checker;
+       GtkUIManager *manager;
+       GtkActionGroup *action_group;
+       GList *list;
+       gchar **languages;
+       guint ii, n_languages;
+       gboolean visible;
+       guint merge_id;
+       gint loc, len;
+
+       view = e_html_editor_get_view (editor);
+       spell_checker = e_html_editor_view_get_spell_checker (view);
+
+       web_view = WEBKIT_WEB_VIEW (view);
+       manager = e_html_editor_get_ui_manager (editor);
+
+       editor->priv->image = NULL;
+       editor->priv->table_cell = NULL;
+
+       /* Update context menu item visibility. */
+       hit_test = webkit_web_view_get_hit_test_result (web_view, event);
+       g_object_get (
+               G_OBJECT (hit_test),
+               "context", &context,
+               "inner-node", &node, NULL);
+       g_object_unref (hit_test);
+
+       visible = (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE);
+       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_IMAGE), visible);
+       if (visible)
+               editor->priv->image = node;
+
+       visible = (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK);
+       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_LINK), visible);
+
+       visible = (WEBKIT_DOM_IS_HTMLHR_ELEMENT (node));
+       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_RULE), visible);
+
+       visible = (webkit_dom_node_get_node_type (node) == 3);
+       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_TEXT), visible);
+
+       visible =
+               gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_IMAGE)) ||
+               gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_LINK)) ||
+               gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_TEXT));
+       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_PARAGRAPH), visible);
+
+       /* Set to visible if any of these are true:
+        *   - Selection is active and contains a link.
+        *   - Cursor is on a link.
+        *   - Cursor is on an image that has a URL or target.
+        */
+       visible = (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) ||
+               (e_html_editor_dom_node_find_parent_element (node, "A") != NULL));
+       gtk_action_set_visible (ACTION (CONTEXT_REMOVE_LINK), visible);
+
+       visible = (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node) ||
+               (e_html_editor_dom_node_find_parent_element (node, "TD") != NULL) ||
+               (e_html_editor_dom_node_find_parent_element (node, "TH") != NULL));
+       gtk_action_set_visible (ACTION (CONTEXT_DELETE_CELL), visible);
+       gtk_action_set_visible (ACTION (CONTEXT_DELETE_COLUMN), visible);
+       gtk_action_set_visible (ACTION (CONTEXT_DELETE_ROW), visible);
+       gtk_action_set_visible (ACTION (CONTEXT_DELETE_TABLE), visible);
+       gtk_action_set_visible (ACTION (CONTEXT_INSERT_COLUMN_AFTER), visible);
+       gtk_action_set_visible (ACTION (CONTEXT_INSERT_COLUMN_BEFORE), visible);
+       gtk_action_set_visible (ACTION (CONTEXT_INSERT_ROW_ABOVE), visible);
+       gtk_action_set_visible (ACTION (CONTEXT_INSERT_ROW_BELOW), visible);
+       gtk_action_set_visible (ACTION (CONTEXT_INSERT_TABLE), visible);
+       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_CELL), visible);
+       if (visible)
+               editor->priv->table_cell = node;
+
+       /* Note the |= (cursor must be in a table cell). */
+       visible |= (WEBKIT_DOM_IS_HTML_TABLE_ELEMENT (node) ||
+               (e_html_editor_dom_node_find_parent_element (node, "TABLE") != NULL));
+       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_TABLE), visible);
+
+       /********************** Spell Check Suggestions **********************/
+
+       action_group = editor->priv->suggestion_actions;
+
+       /* Remove the old content from the context menu. */
+       merge_id = editor->priv->spell_suggestions_merge_id;
+       if (merge_id > 0) {
+               gtk_ui_manager_remove_ui (manager, merge_id);
+               editor->priv->spell_suggestions_merge_id = 0;
+       }
+
+       /* Clear the action group for spelling suggestions. */
+       list = gtk_action_group_list_actions (action_group);
+       while (list != NULL) {
+               GtkAction *action = list->data;
+
+               gtk_action_group_remove_action (action_group, action);
+               list = g_list_delete_link (list, list);
+       }
+
+       languages = e_spell_checker_list_active_languages (
+               spell_checker, &n_languages);
+
+       /* Decide if we should show spell checking items. */
+       checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+       selection = e_html_editor_view_get_selection (view);
+       visible = FALSE;
+       if ((n_languages > 0) && e_html_editor_selection_has_text (selection)) {
+               gchar *word = e_html_editor_selection_get_caret_word (selection);
+               if (word && *word) {
+                       webkit_spell_checker_check_spelling_of_string (
+                               checker, word, &loc, &len);
+                       visible = (loc > -1);
+               } else {
+                       visible = FALSE;
+               }
+               g_free (word);
+       }
+
+       action_group = editor->priv->spell_check_actions;
+       gtk_action_group_set_visible (action_group, visible);
+
+       /* Exit early if spell checking items are invisible. */
+       if (!visible) {
+               g_strfreev (languages);
+               return;
+       }
+
+       merge_id = gtk_ui_manager_new_merge_id (manager);
+       editor->priv->spell_suggestions_merge_id = merge_id;
+
+       /* Handle a single active language as a special case. */
+       if (n_languages == 1) {
+               html_editor_inline_spelling_suggestions (editor);
+               g_strfreev (languages);
+               return;
+       }
+
+       /* Add actions and context menu content for active languages. */
+       for (ii = 0; ii < n_languages; ii++)
+               html_editor_spell_checkers_foreach (editor, languages[ii]);
+
+       g_strfreev (languages);
+}
+
+static void
+html_editor_spell_languages_changed (EHTMLEditor *editor)
+{
+       EHTMLEditorView *view;
+       ESpellChecker *spell_checker;
+       WebKitWebSettings *settings;
+       gchar *comma_separated;
+       gchar **languages;
+
+       view = e_html_editor_get_view (editor);
+       spell_checker = e_html_editor_view_get_spell_checker (view);
+
+       languages = e_spell_checker_list_active_languages (spell_checker, NULL);
+       comma_separated = g_strjoinv (",", languages);
+       g_strfreev (languages);
+
+       /* Set the languages for webview to highlight misspelled words */
+       settings = webkit_web_view_get_settings (
+               WEBKIT_WEB_VIEW (editor->priv->html_editor_view));
+
+       g_object_set (
+               G_OBJECT (settings),
+               "spell-checking-languages", comma_separated,
+               NULL);
+
+       if (editor->priv->spell_check_dialog != NULL)
+               e_html_editor_spell_check_dialog_update_dictionaries (
+                       E_HTML_EDITOR_SPELL_CHECK_DIALOG (
+                       editor->priv->spell_check_dialog));
+
+       if (*comma_separated)
+               e_html_editor_view_force_spell_check (editor->priv->html_editor_view);
+       else
+               e_html_editor_view_turn_spell_check_off (editor->priv->html_editor_view);
+
+       g_free (comma_separated);
+}
+
+static gboolean
+html_editor_show_popup (EHTMLEditor *editor,
+                   GdkEventButton *event,
+                   gpointer user_data)
+{
+       GtkWidget *menu;
+
+       menu = e_html_editor_get_managed_widget (editor, "/context-menu");
+
+       g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, event);
+
+       if (event != NULL)
+               gtk_menu_popup (
+                       GTK_MENU (menu), NULL, NULL, NULL,
+                       user_data, event->button, event->time);
+       else
+               gtk_menu_popup (
+                       GTK_MENU (menu), NULL, NULL, NULL,
+                       user_data, 0, gtk_get_current_event_time ());
+
+       return TRUE;
+}
+
+static gchar *
+html_editor_find_ui_file (const gchar *basename)
+{
+       gchar *filename;
+
+       g_return_val_if_fail (basename != NULL, NULL);
+
+       /* Support running directly from the source tree. */
+       filename = g_build_filename (".", basename, NULL);
+       if (g_file_test (filename, G_FILE_TEST_EXISTS))
+               return filename;
+       g_free (filename);
+
+       /* XXX This is kinda broken. */
+       filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);
+       if (g_file_test (filename, G_FILE_TEST_EXISTS))
+               return filename;
+       g_free (filename);
+
+       g_critical ("Could not locate '%s'", basename);
+
+       return NULL;
+}
+
+static void
+html_editor_parent_changed (GtkWidget *widget,
+                       GtkWidget *previous_parent)
+{
+       GtkWidget *top_level;
+       EHTMLEditor *editor = E_HTML_EDITOR (widget);
+
+       /* If he now have a window, then install our accelators to it */
+       top_level = gtk_widget_get_toplevel (widget);
+       if (GTK_IS_WINDOW (top_level)) {
+               gtk_window_add_accel_group (
+                       GTK_WINDOW (top_level),
+                       gtk_ui_manager_get_accel_group (editor->priv->manager));
+       }
+}
+
+static void
+html_editor_set_property (GObject *object,
+                     guint property_id,
+                     const GValue *value,
+                     GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_FILENAME:
+                       e_html_editor_set_filename (
+                               E_HTML_EDITOR (object),
+                               g_value_get_string (value));
+                       return;
+
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_get_property (GObject *object,
+                     guint property_id,
+                     GValue *value,
+                     GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_FILENAME:
+                       g_value_set_string (
+                               value, e_html_editor_get_filename (
+                               E_HTML_EDITOR (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_constructed (GObject *object)
+{
+       EHTMLEditor *editor = E_HTML_EDITOR (object);
+       EHTMLEditorPrivate *priv = editor->priv;
+       GtkIMMulticontext *im_context;
+
+       GtkWidget *widget;
+       GtkToolbar *toolbar;
+       GtkToolItem *tool_item;
+
+       /* Construct the editing toolbars. */
+
+       widget = e_html_editor_get_managed_widget (editor, "/edit-toolbar");
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_toolbar_set_style (GTK_TOOLBAR (widget), GTK_TOOLBAR_BOTH_HORIZ);
+       gtk_grid_attach (GTK_GRID (editor), widget, 0, 0, 1, 1);
+       priv->edit_toolbar = g_object_ref (widget);
+       gtk_widget_show (widget);
+
+       widget = e_html_editor_get_managed_widget (editor, "/html-toolbar");
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_toolbar_set_style (GTK_TOOLBAR (widget), GTK_TOOLBAR_BOTH_HORIZ);
+       gtk_grid_attach (GTK_GRID (editor), widget, 0, 1, 1, 1);
+       priv->html_toolbar = g_object_ref (widget);
+       gtk_widget_show (widget);
+
+       /* Construct the activity bar. */
+
+       widget = e_activity_bar_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_grid_attach (GTK_GRID (editor), widget, 0, 2, 1, 1);
+       priv->activity_bar = g_object_ref (widget);
+
+       /* Construct the alert bar for errors. */
+
+       widget = e_alert_bar_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_grid_attach (GTK_GRID (editor), widget, 0, 3, 1, 1);
+       priv->alert_bar = g_object_ref (widget);
+       /* EAlertBar controls its own visibility. */
+
+       /* Construct the main editing area. */
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (
+               GTK_SCROLLED_WINDOW (widget),
+               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_shadow_type (
+               GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_widget_set_vexpand (widget, TRUE);
+       gtk_grid_attach (GTK_GRID (editor), widget, 0, 4, 1, 1);
+       priv->scrolled_window = g_object_ref (widget);
+       gtk_widget_show (widget);
+
+       widget = GTK_WIDGET (e_html_editor_get_view (editor));
+       gtk_container_add (GTK_CONTAINER (priv->scrolled_window), widget);
+       gtk_widget_show (widget);
+       g_signal_connect_swapped (
+               widget, "popup-event",
+               G_CALLBACK (html_editor_show_popup), editor);
+
+       /* Add some combo boxes to the "edit" toolbar. */
+
+       toolbar = GTK_TOOLBAR (priv->edit_toolbar);
+
+       tool_item = gtk_tool_item_new ();
+       widget = e_action_combo_box_new_with_action (
+               GTK_RADIO_ACTION (ACTION (STYLE_NORMAL)));
+       gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+       gtk_container_add (GTK_CONTAINER (tool_item), widget);
+       gtk_widget_set_tooltip_text (widget, _("Paragraph Style"));
+       gtk_toolbar_insert (toolbar, tool_item, 0);
+       priv->style_combo_box = g_object_ref (widget);
+       gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+       tool_item = gtk_separator_tool_item_new ();
+       gtk_toolbar_insert (toolbar, tool_item, 0);
+       gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+       tool_item = gtk_tool_item_new ();
+       widget = e_action_combo_box_new_with_action (
+               GTK_RADIO_ACTION (ACTION (MODE_HTML)));
+       gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+       gtk_container_add (GTK_CONTAINER (tool_item), widget);
+       gtk_widget_set_tooltip_text (widget, _("Editing Mode"));
+       gtk_toolbar_insert (toolbar, tool_item, 0);
+       priv->mode_combo_box = g_object_ref (widget);
+       gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+       /* Add some combo boxes to the "html" toolbar. */
+
+       toolbar = GTK_TOOLBAR (priv->html_toolbar);
+
+       tool_item = gtk_tool_item_new ();
+       widget = e_color_combo_new ();
+       gtk_container_add (GTK_CONTAINER (tool_item), widget);
+       gtk_widget_set_tooltip_text (widget, _("Font Color"));
+       gtk_toolbar_insert (toolbar, tool_item, 0);
+       priv->color_combo_box = g_object_ref (widget);
+       gtk_widget_show_all (GTK_WIDGET (tool_item));
+       g_object_bind_property (
+               priv->color_combo_box, "current-color",
+               priv->selection, "font-color",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_object_bind_property (
+               priv->html_editor_view, "editable",
+               priv->color_combo_box, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       tool_item = gtk_tool_item_new ();
+       widget = e_action_combo_box_new_with_action (
+               GTK_RADIO_ACTION (ACTION (SIZE_PLUS_ZERO)));
+       gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+       gtk_container_add (GTK_CONTAINER (tool_item), widget);
+       gtk_widget_set_tooltip_text (widget, _("Font Size"));
+       gtk_toolbar_insert (toolbar, tool_item, 0);
+       priv->size_combo_box = g_object_ref (widget);
+       gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+       /* Add input methods to the context menu. */
+       widget = e_html_editor_get_managed_widget (
+               editor, "/context-menu/context-input-methods-menu");
+       widget = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+       g_object_get (
+               G_OBJECT (priv->html_editor_view), "im-context", &im_context, NULL);
+       gtk_im_multicontext_append_menuitems (
+               GTK_IM_MULTICONTEXT (im_context),
+               GTK_MENU_SHELL (widget));
+}
+
+static void
+html_editor_dispose (GObject *object)
+{
+       EHTMLEditorPrivate *priv;
+
+       priv = E_HTML_EDITOR_GET_PRIVATE (object);
+
+       g_clear_object (&priv->manager);
+       g_clear_object (&priv->core_actions);
+       g_clear_object (&priv->html_actions);
+       g_clear_object (&priv->context_actions);
+       g_clear_object (&priv->html_context_actions);
+       g_clear_object (&priv->language_actions);
+       g_clear_object (&priv->spell_check_actions);
+       g_clear_object (&priv->suggestion_actions);
+
+       g_clear_object (&priv->main_menu);
+       g_clear_object (&priv->main_toolbar);
+       g_clear_object (&priv->edit_toolbar);
+       g_clear_object (&priv->html_toolbar);
+       g_clear_object (&priv->activity_bar);
+       g_clear_object (&priv->alert_bar);
+       g_clear_object (&priv->edit_area);
+
+       g_clear_object (&priv->color_combo_box);
+       g_clear_object (&priv->mode_combo_box);
+       g_clear_object (&priv->size_combo_box);
+       g_clear_object (&priv->style_combo_box);
+       g_clear_object (&priv->scrolled_window);
+
+       g_clear_object (&priv->html_editor_view);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_html_editor_parent_class)->dispose (object);
+}
+
+static void
+html_editor_submit_alert (EAlertSink *alert_sink,
+                     EAlert *alert)
+{
+       EHTMLEditorPrivate *priv;
+       EAlertBar *alert_bar;
+       GtkWidget *toplevel;
+       GtkWidget *widget;
+       GtkWindow *parent;
+
+       priv = E_HTML_EDITOR_GET_PRIVATE (alert_sink);
+
+       switch (e_alert_get_message_type (alert)) {
+               case GTK_MESSAGE_INFO:
+               case GTK_MESSAGE_WARNING:
+               case GTK_MESSAGE_ERROR:
+                       alert_bar = E_ALERT_BAR (priv->alert_bar);
+                       e_alert_bar_add_alert (alert_bar, alert);
+                       break;
+
+               default:
+                       widget = GTK_WIDGET (alert_sink);
+                       toplevel = gtk_widget_get_toplevel (widget);
+                       if (GTK_IS_WINDOW (toplevel))
+                               parent = GTK_WINDOW (toplevel);
+                       else
+                               parent = NULL;
+                       widget = e_alert_dialog_new (parent, alert);
+                       gtk_dialog_run (GTK_DIALOG (widget));
+                       gtk_widget_destroy (widget);
+       }
+}
+
+static void
+e_html_editor_class_init (EHTMLEditorClass *class)
+{
+       GObjectClass *object_class;
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = html_editor_set_property;
+       object_class->get_property = html_editor_get_property;
+       object_class->constructed = html_editor_constructed;
+       object_class->dispose = html_editor_dispose;
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->parent_set = html_editor_parent_changed;
+
+       class->update_actions = html_editor_update_actions;
+       class->spell_languages_changed = html_editor_spell_languages_changed;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_FILENAME,
+               g_param_spec_string (
+                       "filename",
+                       NULL,
+                       NULL,
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       signals[UPDATE_ACTIONS] = g_signal_new (
+               "update-actions",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EHTMLEditorClass, update_actions),
+               NULL, NULL,
+               g_cclosure_marshal_VOID__BOXED,
+               G_TYPE_NONE, 1,
+               GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+       signals[SPELL_LANGUAGES_CHANGED] = g_signal_new (
+               "spell-languages-changed",
+               G_OBJECT_CLASS_TYPE (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EHTMLEditorClass, spell_languages_changed),
+               NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+}
+
+static void
+e_html_editor_alert_sink_init (EAlertSinkInterface *interface)
+{
+       interface->submit_alert = html_editor_submit_alert;
+}
+
+static void
+e_html_editor_init (EHTMLEditor *editor)
+{
+       EHTMLEditorPrivate *priv;
+       GtkWidget *widget;
+       gchar *filename;
+       GError *error = NULL;
+
+       editor->priv = E_HTML_EDITOR_GET_PRIVATE (editor);
+
+       priv = editor->priv;
+
+       priv->manager = gtk_ui_manager_new ();
+       priv->core_actions = gtk_action_group_new ("core");
+       priv->html_actions = gtk_action_group_new ("html");
+       priv->context_actions = gtk_action_group_new ("core-context");
+       priv->html_context_actions = gtk_action_group_new ("html-context");
+       priv->language_actions = gtk_action_group_new ("language");
+       priv->spell_check_actions = gtk_action_group_new ("spell-check");
+       priv->suggestion_actions = gtk_action_group_new ("suggestion");
+       priv->html_editor_view = g_object_ref_sink (e_html_editor_view_new ());
+       priv->selection = e_html_editor_view_get_selection (priv->html_editor_view);
+
+       filename = html_editor_find_ui_file ("e-html-editor-manager.ui");
+       if (!gtk_ui_manager_add_ui_from_file (priv->manager, filename, &error)) {
+               g_critical ("Couldn't load builder file: %s\n", error->message);
+               g_clear_error (&error);
+       }
+       g_free (filename);
+
+       editor_actions_init (editor);
+       priv->editor_layout_row = 2;
+
+       /* Tweak the main-toolbar style. */
+       widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
+       gtk_style_context_add_class (
+               gtk_widget_get_style_context (widget),
+               GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
+}
+
+/**
+ * e_html_editor_new:
+ *
+ * Constructs a new #EHTMLEditor.
+ *
+ * Returns: A newly created widget. [transfer-full]
+ */
+GtkWidget *
+e_html_editor_new (void)
+{
+       return g_object_new (E_TYPE_HTML_EDITOR, NULL);
+}
+
+/**
+ * e_html_editor_get_view:
+ * @editor: an #EHTMLEditor
+ *
+ * Returns instance of #EHTMLEditorView used in the @editor.
+ */
+EHTMLEditorView *
+e_html_editor_get_view (EHTMLEditor *editor)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+       return editor->priv->html_editor_view;
+}
+
+/**
+ * e_html_editor_get_ui_manager:
+ * @editor: an #EHTMLEditor
+ *
+ * Returns #GtkUIManager that manages all the actions in the @editor.
+ */
+GtkUIManager *
+e_html_editor_get_ui_manager (EHTMLEditor *editor)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+       return editor->priv->manager;
+}
+
+/**
+ * e_html_editor_get_action:
+ * @editor: an #EHTMLEditor
+ * @action_name: name of action to lookup and return
+ *
+ * Returns: A #GtkAction matching @action_name or @NULL if no such action exists.
+ */
+GtkAction *
+e_html_editor_get_action (EHTMLEditor *editor,
+                     const gchar *action_name)
+{
+       GtkUIManager *manager;
+       GtkAction *action = NULL;
+       GList *list;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+       g_return_val_if_fail (action_name != NULL, NULL);
+
+       manager = e_html_editor_get_ui_manager (editor);
+       list = gtk_ui_manager_get_action_groups (manager);
+
+       while (list != NULL && action == NULL) {
+               GtkActionGroup *action_group = list->data;
+
+               action = gtk_action_group_get_action (
+                       action_group, action_name);
+
+               list = g_list_next (list);
+       }
+
+       g_return_val_if_fail (action != NULL, NULL);
+
+       return action;
+}
+
+/**
+ * e_html_editor_get_action_group:
+ * @editor: an #EHTMLEditor
+ * @group_name: name of action group to lookup and return
+ *
+ * Returns: A #GtkActionGroup matching @group_name or @NULL if not such action
+ * group exists.
+ */
+GtkActionGroup *
+e_html_editor_get_action_group (EHTMLEditor *editor,
+                           const gchar *group_name)
+{
+       GtkUIManager *manager;
+       GList *list;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+       g_return_val_if_fail (group_name != NULL, NULL);
+
+       manager = e_html_editor_get_ui_manager (editor);
+       list = gtk_ui_manager_get_action_groups (manager);
+
+       while (list != NULL) {
+               GtkActionGroup *action_group = list->data;
+               const gchar *name;
+
+               name = gtk_action_group_get_name (action_group);
+               if (strcmp (name, group_name) == 0)
+                       return action_group;
+
+               list = g_list_next (list);
+       }
+
+       return NULL;
+}
+
+GtkWidget *
+e_html_editor_get_managed_widget (EHTMLEditor *editor,
+                             const gchar *widget_path)
+{
+       GtkUIManager *manager;
+       GtkWidget *widget;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+       g_return_val_if_fail (widget_path != NULL, NULL);
+
+       manager = e_html_editor_get_ui_manager (editor);
+       widget = gtk_ui_manager_get_widget (manager, widget_path);
+
+       g_return_val_if_fail (widget != NULL, NULL);
+
+       return widget;
+}
+
+GtkWidget *
+e_html_editor_get_style_combo_box (EHTMLEditor *editor)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+       return editor->priv->style_combo_box;
+}
+
+/**
+ * e_html_editor_get_filename:
+ * @editor: an #EHTMLEditor
+ *
+ * Returns path and name of file to which content of the editor should be saved.
+ */
+const gchar *
+e_html_editor_get_filename (EHTMLEditor *editor)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+       return editor->priv->filename;
+}
+
+/**
+ * e_html_editor_set_filename:
+ * @editor: an #EHTMLEditor
+ * @filename: Target file
+ *
+ * Sets file to which content of the editor should be saved (see
+ * e_html_editor_save()).
+ */
+void
+e_html_editor_set_filename (EHTMLEditor *editor,
+                       const gchar *filename)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+       if (g_strcmp0 (editor->priv->filename, filename) == 0)
+               return;
+
+       g_free (editor->priv->filename);
+       editor->priv->filename = g_strdup (filename);
+
+       g_object_notify (G_OBJECT (editor), "filename");
+}
+
+EActivityBar *
+e_html_editor_get_activity_bar (EHTMLEditor *editor)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+       return E_ACTIVITY_BAR (editor->priv->activity_bar);
+}
+
+/**
+ * e_html_editor_new_activity:
+ * @editor: an #EHTMLEditor
+ *
+ * Creates and configures a new #EActivity so its progress is shown in
+ * the @editor.  The #EActivity comes pre-loaded with a #CamelOperation.
+ *
+ * Returns: a new #EActivity for use with @editor
+ **/
+EActivity *
+e_html_editor_new_activity (EHTMLEditor *editor)
+{
+       EActivity *activity;
+       EActivityBar *activity_bar;
+       GCancellable *cancellable;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+       activity = e_activity_new ();
+       e_activity_set_alert_sink (activity, E_ALERT_SINK (editor));
+
+       cancellable = camel_operation_new ();
+       e_activity_set_cancellable (activity, cancellable);
+       g_object_unref (cancellable);
+
+       activity_bar = E_ACTIVITY_BAR (editor->priv->activity_bar);
+       e_activity_bar_set_activity (activity_bar, activity);
+
+       return activity;
+}
+
+/**
+ * e_html_editor_pack_above:
+ * @editor: an #EHTMLEditor
+ * @child: a #GtkWidget
+ *
+ * Inserts @child right between the toolbars and the editor widget itself.
+ */
+void
+e_html_editor_pack_above (EHTMLEditor *editor,
+                     GtkWidget *child)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+       g_return_if_fail (GTK_IS_WIDGET (child));
+
+       gtk_grid_insert_row (GTK_GRID (editor), editor->priv->editor_layout_row);
+       gtk_grid_attach (GTK_GRID (editor), child, 0, editor->priv->editor_layout_row, 1, 1);
+       editor->priv->editor_layout_row++;
+}
+
+/**
+ * e_html_editor_save:
+ * @editor: an #EHTMLEditor
+ * @filename: file into which to save the content
+ * @as_html: whether the content should be saved as HTML or plain text
+ * @error:[out] a #GError
+ *
+ * Saves current content of the #EHTMLEditorView into given file. When @as_html
+ * is @FALSE, the content is first converted into plain text.
+ *
+ * Returns: @TRUE when content is succesfully saved, @FALSE otherwise.
+ */
+gboolean
+e_html_editor_save (EHTMLEditor *editor,
+               const gchar *filename,
+               gboolean as_html,
+               GError **error)
+{
+       GFile *file;
+       GFileOutputStream *stream;
+       gchar *content;
+       gsize written;
+
+       file = g_file_new_for_path (filename);
+       stream = g_file_replace (
+               file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error);
+       if ((error && *error) || !stream)
+               return FALSE;
+
+       if (as_html)
+               content = e_html_editor_view_get_text_html (
+                       E_HTML_EDITOR_VIEW (editor));
+       else
+               content = e_html_editor_view_get_text_plain (
+                       E_HTML_EDITOR_VIEW (editor));
+
+       if (!content || !*content) {
+               g_set_error (
+                       error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Failed to obtain content of editor");
+               return FALSE;
+       }
+
+       g_output_stream_write_all (
+               G_OUTPUT_STREAM (stream), content, strlen (content),
+               &written, NULL, error);
+
+       g_free (content);
+       g_object_unref (stream);
+       g_object_unref (file);
+
+       return TRUE;
+}
+
diff --git a/e-util/e-html-editor.h b/e-util/e-html-editor.h
new file mode 100644
index 0000000..5618cc8
--- /dev/null
+++ b/e-util/e-html-editor.h
@@ -0,0 +1,108 @@
+/*
+ * e-html-editor.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HTML_EDITOR_H
+#define E_HTML_EDITOR_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-activity.h>
+#include <e-util/e-activity-bar.h>
+#include <e-util/e-html-editor-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HTML_EDITOR \
+       (e_html_editor_get_type ())
+#define E_HTML_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR, EHTMLEditor))
+#define E_HTML_EDITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR, EHTMLEditorClass))
+#define E_IS_HTML_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR))
+#define E_IS_HTML_EDITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR))
+#define E_HTML_EDITOR_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR, EHTMLEditorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditor EHTMLEditor;
+typedef struct _EHTMLEditorClass EHTMLEditorClass;
+typedef struct _EHTMLEditorPrivate EHTMLEditorPrivate;
+
+struct _EHTMLEditor {
+       GtkGrid parent;
+       EHTMLEditorPrivate *priv;
+};
+
+struct _EHTMLEditorClass {
+       GtkGridClass parent_class;
+
+       void            (*update_actions)       (EHTMLEditor *editor,
+                                                GdkEventButton *event);
+       void            (*spell_languages_changed)
+                                               (EHTMLEditor *editor);
+};
+
+GType          e_html_editor_get_type          (void) G_GNUC_CONST;
+GtkWidget *    e_html_editor_new               (void);
+EHTMLEditorView *
+               e_html_editor_get_view          (EHTMLEditor *editor);
+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,
+                                                const gchar *action_name);
+GtkActionGroup *e_html_editor_get_action_group (EHTMLEditor *editor,
+                                                const gchar *group_name);
+GtkWidget *    e_html_editor_get_widget        (EHTMLEditor *editor,
+                                                const gchar *widget_name);
+GtkWidget *    e_html_editor_get_managed_widget
+                                               (EHTMLEditor *editor,
+                                                const gchar *widget_path);
+GtkWidget *    e_html_editor_get_style_combo_box
+                                               (EHTMLEditor *editor);
+const gchar *  e_html_editor_get_filename      (EHTMLEditor *editor);
+void           e_html_editor_set_filename      (EHTMLEditor *editor,
+                                                const gchar *filename);
+EActivityBar * e_html_editor_get_activity_bar  (EHTMLEditor *editor);
+EActivity *    e_html_editor_new_activity      (EHTMLEditor *editor);
+void           e_html_editor_pack_above        (EHTMLEditor *editor,
+                                                GtkWidget *child);
+
+/*****************************************************************************
+ * High-Level Editing Interface
+ *****************************************************************************/
+
+gboolean       e_html_editor_save              (EHTMLEditor *editor,
+                                                const gchar *filename,
+                                                gboolean as_html,
+                                                GError **error);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_H */
diff --git a/e-util/e-image-chooser-dialog.c b/e-util/e-image-chooser-dialog.c
new file mode 100644
index 0000000..73a6c20
--- /dev/null
+++ b/e-util/e-image-chooser-dialog.c
@@ -0,0 +1,223 @@
+/*
+ * e-image-chooser-dialog.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "e-image-chooser-dialog.h"
+
+#define E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_IMAGE_CHOOSER_DIALOG, EImageChooserDialogPrivate))
+
+#define PREVIEW_WIDTH  256
+#define PREVIEW_HEIGHT 256
+
+typedef struct _Context Context;
+
+struct _EImageChooserDialogPrivate {
+       GCancellable *cancellable;
+};
+
+struct _Context {
+       GtkFileChooser *file_chooser;
+       GCancellable *cancellable;
+};
+
+G_DEFINE_TYPE (
+       EImageChooserDialog,
+       e_image_chooser_dialog,
+       GTK_TYPE_FILE_CHOOSER_DIALOG)
+
+static void
+context_free (Context *context)
+{
+       g_object_unref (context->file_chooser);
+       g_object_unref (context->cancellable);
+
+       g_slice_free (Context, context);
+}
+
+static void
+image_chooser_dialog_read_cb (GFile *preview_file,
+                              GAsyncResult *result,
+                              Context *context)
+{
+       GdkPixbuf *pixbuf;
+       GtkWidget *preview_widget;
+       GFileInputStream *input_stream;
+
+       input_stream = g_file_read_finish (preview_file, result, NULL);
+
+       /* FIXME Handle errors better, but remember
+        *       to ignore G_IO_ERROR_CANCELLED. */
+       if (input_stream == NULL)
+               goto exit;
+
+       /* XXX This blocks, but GDK-PixBuf offers no asynchronous
+        *     alternative and I don't want to deal with making GDK
+        *     calls from threads and all the crap that goes with it. */
+       pixbuf = gdk_pixbuf_new_from_stream_at_scale (
+               G_INPUT_STREAM (input_stream),
+               PREVIEW_WIDTH, PREVIEW_HEIGHT, TRUE,
+               context->cancellable, NULL);
+
+       preview_widget = gtk_file_chooser_get_preview_widget (
+               context->file_chooser);
+
+       gtk_file_chooser_set_preview_widget_active (
+               context->file_chooser, pixbuf != NULL);
+
+       gtk_image_set_from_pixbuf (GTK_IMAGE (preview_widget), pixbuf);
+
+       if (pixbuf != NULL)
+               g_object_unref (pixbuf);
+
+       g_object_unref (input_stream);
+
+exit:
+       context_free (context);
+}
+
+static void
+image_chooser_dialog_update_preview (GtkFileChooser *file_chooser)
+{
+       EImageChooserDialogPrivate *priv;
+       GtkWidget *preview_widget;
+       GFile *preview_file;
+       Context *context;
+
+       priv = E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE (file_chooser);
+       preview_file = gtk_file_chooser_get_preview_file (file_chooser);
+       preview_widget = gtk_file_chooser_get_preview_widget (file_chooser);
+
+       if (priv->cancellable != NULL) {
+               g_cancellable_cancel (priv->cancellable);
+               g_object_unref (priv->cancellable);
+               priv->cancellable = NULL;
+       }
+
+       gtk_image_clear (GTK_IMAGE (preview_widget));
+       gtk_file_chooser_set_preview_widget_active (file_chooser, FALSE);
+
+       if (preview_file == NULL)
+               return;
+
+       priv->cancellable = g_cancellable_new ();
+
+       context = g_slice_new0 (Context);
+       context->file_chooser = g_object_ref (file_chooser);
+       context->cancellable = g_object_ref (priv->cancellable);
+
+       g_file_read_async (
+               preview_file, G_PRIORITY_LOW,
+               priv->cancellable, (GAsyncReadyCallback)
+               image_chooser_dialog_read_cb, context);
+
+       g_object_unref (preview_file);
+}
+
+static void
+image_chooser_dialog_dispose (GObject *object)
+{
+       EImageChooserDialogPrivate *priv;
+
+       priv = E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE (object);
+
+       if (priv->cancellable != NULL) {
+               g_cancellable_cancel (priv->cancellable);
+               g_object_unref (priv->cancellable);
+               priv->cancellable = NULL;
+       }
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_image_chooser_dialog_parent_class)->dispose (object);
+}
+
+static void
+image_chooser_dialog_constructed (GObject *object)
+{
+       GtkFileChooser *file_chooser;
+       GtkFileFilter *file_filter;
+
+       file_chooser = GTK_FILE_CHOOSER (object);
+       gtk_file_chooser_set_local_only (file_chooser, FALSE);
+
+       gtk_dialog_add_button (
+               GTK_DIALOG (file_chooser),
+               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+       gtk_dialog_add_button (
+               GTK_DIALOG (file_chooser),
+               GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT);
+       gtk_dialog_set_default_response (
+               GTK_DIALOG (file_chooser), GTK_RESPONSE_ACCEPT);
+
+       file_filter = gtk_file_filter_new ();
+       gtk_file_filter_add_pixbuf_formats (file_filter);
+       gtk_file_chooser_set_filter (file_chooser, file_filter);
+
+       gtk_file_chooser_set_preview_widget (file_chooser, gtk_image_new ());
+}
+
+static void
+e_image_chooser_dialog_class_init (EImageChooserDialogClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (
+               class, sizeof (EImageChooserDialogPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->dispose = image_chooser_dialog_dispose;
+       object_class->constructed = image_chooser_dialog_constructed;
+}
+
+static void
+e_image_chooser_dialog_init (EImageChooserDialog *dialog)
+{
+       dialog->priv = E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE (dialog);
+
+       g_signal_connect (
+               dialog, "update-preview",
+               G_CALLBACK (image_chooser_dialog_update_preview), NULL);
+}
+
+GtkWidget *
+e_image_chooser_dialog_new (const gchar *title,
+                            GtkWindow *parent)
+{
+       return g_object_new (
+               E_TYPE_IMAGE_CHOOSER_DIALOG,
+               "action", GTK_FILE_CHOOSER_ACTION_OPEN,
+               "title", title,
+               "transient-for", parent, NULL);
+}
+
+GFile *
+e_image_chooser_dialog_run (EImageChooserDialog *dialog)
+{
+       GtkFileChooser *file_chooser;
+
+       g_return_val_if_fail (E_IS_IMAGE_CHOOSER_DIALOG (dialog), NULL);
+
+       file_chooser = GTK_FILE_CHOOSER (dialog);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
+               return NULL;
+
+       return gtk_file_chooser_get_file (file_chooser);
+}
diff --git a/e-util/e-image-chooser-dialog.h b/e-util/e-image-chooser-dialog.h
new file mode 100644
index 0000000..967fddf
--- /dev/null
+++ b/e-util/e-image-chooser-dialog.h
@@ -0,0 +1,74 @@
+/*
+ * e-image-chooser-dialog.h
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_IMAGE_CHOOSER_DIALOG_H
+#define E_IMAGE_CHOOSER_DIALOG_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_IMAGE_CHOOSER_DIALOG \
+       (e_image_chooser_dialog_get_type ())
+#define E_IMAGE_CHOOSER_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_IMAGE_CHOOSER_DIALOG, EImageChooserDialog))
+#define E_IMAGE_CHOOSER_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_IMAGE_CHOOSER_DIALOG, EImageChooserDialogClass))
+#define E_IS_IMAGE_CHOOSER_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_IMAGE_CHOOSER_DIALOG))
+#define E_IS_IMAGE_CHOOSER_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_IMAGE_CHOOSER_DIALOG))
+#define E_IMAGE_CHOOSER_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_IMAGE_CHOOSER_DIALOG, EImageChooserDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EImageChooserDialog EImageChooserDialog;
+typedef struct _EImageChooserDialogClass EImageChooserDialogClass;
+typedef struct _EImageChooserDialogPrivate EImageChooserDialogPrivate;
+
+struct _EImageChooserDialog {
+       GtkFileChooserDialog parent;
+       EImageChooserDialogPrivate *priv;
+};
+
+struct _EImageChooserDialogClass {
+       GtkFileChooserDialogClass parent_class;
+};
+
+GType          e_image_chooser_dialog_get_type
+                                       (void) G_GNUC_CONST;
+GtkWidget *    e_image_chooser_dialog_new
+                                       (const gchar *title,
+                                        GtkWindow *parent);
+GFile *                e_image_chooser_dialog_run
+                                       (EImageChooserDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_IMAGE_CHOOSER_DIALOG_H */
diff --git a/e-util/e-mail-signature-editor.c b/e-util/e-mail-signature-editor.c
index 05c783d..b5c8763 100644
--- a/e-util/e-mail-signature-editor.c
+++ b/e-util/e-mail-signature-editor.c
@@ -17,13 +17,13 @@
 
 #include "e-mail-signature-editor.h"
 
+#include <config.h>
 #include <string.h>
 #include <glib/gi18n.h>
 
-#include "e-alert-bar.h"
 #include "e-alert-dialog.h"
 #include "e-alert-sink.h"
-#include "e-web-view-gtkhtml.h"
+#include "e-alert-bar.h"
 
 #define E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
@@ -32,6 +32,7 @@
 typedef struct _AsyncContext AsyncContext;
 
 struct _EMailSignatureEditorPrivate {
+       EHTMLEditor *editor;
        GtkActionGroup *action_group;
        EFocusTracker *focus_tracker;
        GCancellable *cancellable;
@@ -40,7 +41,6 @@ struct _EMailSignatureEditorPrivate {
        gchar *original_name;
 
        GtkWidget *entry;               /* not referenced */
-       GtkWidget *alert_bar;           /* not referenced */
 };
 
 struct _AsyncContext {
@@ -52,6 +52,7 @@ struct _AsyncContext {
 
 enum {
        PROP_0,
+       PROP_EDITOR,
        PROP_FOCUS_TRACKER,
        PROP_REGISTRY,
        PROP_SOURCE
@@ -75,17 +76,10 @@ static const gchar *ui =
 "  </toolbar>\n"
 "</ui>";
 
-/* Forward Declarations */
-static void    e_mail_signature_editor_alert_sink_init
-                                       (EAlertSinkInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (
+G_DEFINE_TYPE (
        EMailSignatureEditor,
        e_mail_signature_editor,
-       GTKHTML_TYPE_EDITOR,
-       G_IMPLEMENT_INTERFACE (
-               E_TYPE_ALERT_SINK,
-               e_mail_signature_editor_alert_sink_init))
+       GTK_TYPE_WINDOW)
 
 static void
 async_context_free (AsyncContext *async_context)
@@ -106,8 +100,10 @@ mail_signature_editor_loaded_cb (GObject *object,
                                  GAsyncResult *result,
                                  gpointer user_data)
 {
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        ESource *source;
-       EMailSignatureEditor *editor;
+       EMailSignatureEditor *window;
        ESourceMailSignature *extension;
        const gchar *extension_name;
        const gchar *mime_type;
@@ -116,7 +112,7 @@ mail_signature_editor_loaded_cb (GObject *object,
        GError *error = NULL;
 
        source = E_SOURCE (object);
-       editor = E_MAIL_SIGNATURE_EDITOR (user_data);
+       window = E_MAIL_SIGNATURE_EDITOR (user_data);
 
        e_source_mail_signature_load_finish (
                source, result, &contents, NULL, &error);
@@ -124,17 +120,17 @@ mail_signature_editor_loaded_cb (GObject *object,
        /* Ignore cancellations. */
        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
                g_warn_if_fail (contents == NULL);
-               g_object_unref (editor);
+               g_object_unref (window);
                g_error_free (error);
                return;
 
        } else if (error != NULL) {
                g_warn_if_fail (contents == NULL);
                e_alert_submit (
-                       E_ALERT_SINK (editor),
+                       E_ALERT_SINK (window),
                        "widgets:no-load-signature",
                        error->message, NULL);
-               g_object_unref (editor);
+               g_object_unref (window);
                g_error_free (error);
                return;
        }
@@ -147,30 +143,18 @@ mail_signature_editor_loaded_cb (GObject *object,
        mime_type = e_source_mail_signature_get_mime_type (extension);
        is_html = (g_strcmp0 (mime_type, "text/html") == 0);
 
-       gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (editor), is_html);
+       editor = e_mail_signature_editor_get_editor (window);
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_set_html_mode (view, is_html);
 
-       if (is_html) {
-               gtkhtml_editor_insert_html (
-                       GTKHTML_EDITOR (editor), contents);
-       } else {
-               gtkhtml_editor_insert_text (
-                       GTKHTML_EDITOR (editor), contents);
-
-               gtkhtml_editor_run_command (
-                       GTKHTML_EDITOR (editor), "cursor-position-save");
-               gtkhtml_editor_run_command (
-                       GTKHTML_EDITOR (editor), "select-all");
-               gtkhtml_editor_run_command (
-                       GTKHTML_EDITOR (editor), "style-pre");
-               gtkhtml_editor_run_command (
-                       GTKHTML_EDITOR (editor), "unselect-all");
-               gtkhtml_editor_run_command (
-                       GTKHTML_EDITOR (editor), "cursor-position-restore");
-       }
+       if (is_html)
+               e_html_editor_view_set_text_html (view, contents);
+       else
+               e_html_editor_view_set_text_plain (view, contents);
 
        g_free (contents);
 
-       g_object_unref (editor);
+       g_object_unref (window);
 }
 
 static gboolean
@@ -189,28 +173,33 @@ mail_signature_editor_delete_event_cb (EMailSignatureEditor *editor,
 
 static void
 action_close_cb (GtkAction *action,
-                 EMailSignatureEditor *editor)
+                 EMailSignatureEditor *window)
 {
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        gboolean something_changed = FALSE;
        const gchar *original_name;
        const gchar *signature_name;
 
-       original_name = editor->priv->original_name;
-       signature_name = gtk_entry_get_text (GTK_ENTRY (editor->priv->entry));
+       original_name = window->priv->original_name;
+       signature_name = gtk_entry_get_text (GTK_ENTRY (window->priv->entry));
+
+       editor = e_mail_signature_editor_get_editor (window);
+       view = e_html_editor_get_view (editor);
 
-       something_changed |= gtkhtml_editor_has_undo (GTKHTML_EDITOR (editor));
+       something_changed |= webkit_web_view_can_undo (WEBKIT_WEB_VIEW (view));
        something_changed |= (strcmp (signature_name, original_name) != 0);
 
        if (something_changed) {
                gint response;
 
                response = e_alert_run_dialog_for_args (
-                       GTK_WINDOW (editor),
+                       GTK_WINDOW (window),
                        "widgets:ask-signature-changed", NULL);
                if (response == GTK_RESPONSE_YES) {
                        GtkActionGroup *action_group;
 
-                       action_group = editor->priv->action_group;
+                       action_group = window->priv->action_group;
                        action = gtk_action_group_get_action (
                                action_group, "save-and-close");
                        gtk_action_activate (action);
@@ -219,7 +208,7 @@ action_close_cb (GtkAction *action,
                        return;
        }
 
-       gtk_widget_destroy (GTK_WIDGET (editor));
+       gtk_widget_destroy (GTK_WIDGET (window));
 }
 
 static void
@@ -292,8 +281,7 @@ action_save_and_close_cb (GtkAction *action,
                /* Only make sure that the 'source-changed' is called,
                 * thus the preview of the signature is updated on save.
                 * It is not called when only signature body is changed
-                * (and ESource properties are left unchanged).
-               */
+                * (and ESource properties are left unchanged). */
                g_signal_emit_by_name (registry, "source-changed", source);
 
                gtk_widget_destroy (GTK_WIDGET (editor));
@@ -397,6 +385,13 @@ mail_signature_editor_get_property (GObject *object,
                                     GParamSpec *pspec)
 {
        switch (property_id) {
+               case PROP_EDITOR:
+                       g_value_set_object (
+                               value,
+                               e_mail_signature_editor_get_editor (
+                               E_MAIL_SIGNATURE_EDITOR (object)));
+                       return;
+
                case PROP_FOCUS_TRACKER:
                        g_value_set_object (
                                value,
@@ -429,6 +424,11 @@ mail_signature_editor_dispose (GObject *object)
 
        priv = E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE (object);
 
+       if (priv->editor != NULL) {
+               g_object_unref (priv->editor);
+               priv->editor = NULL;
+       }
+
        if (priv->action_group != NULL) {
                g_object_unref (priv->action_group);
                priv->action_group = NULL;
@@ -477,16 +477,18 @@ mail_signature_editor_finalize (GObject *object)
 static void
 mail_signature_editor_constructed (GObject *object)
 {
-       EMailSignatureEditor *editor;
+       EMailSignatureEditor *window;
        GtkActionGroup *action_group;
        EFocusTracker *focus_tracker;
-       GtkhtmlEditor *gtkhtml_editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        GtkUIManager *ui_manager;
        GDBusObject *dbus_object;
        ESource *source;
        GtkAction *action;
        GtkWidget *container;
        GtkWidget *widget;
+       GtkWidget *hbox;
        const gchar *display_name;
        GError *error = NULL;
 
@@ -494,10 +496,11 @@ mail_signature_editor_constructed (GObject *object)
        G_OBJECT_CLASS (e_mail_signature_editor_parent_class)->
                constructed (object);
 
-       editor = E_MAIL_SIGNATURE_EDITOR (object);
+       window = E_MAIL_SIGNATURE_EDITOR (object);
+       editor = e_mail_signature_editor_get_editor (window);
+       view = e_html_editor_get_view (editor);
 
-       gtkhtml_editor = GTKHTML_EDITOR (editor);
-       ui_manager = gtkhtml_editor_get_ui_manager (gtkhtml_editor);
+       ui_manager = e_html_editor_get_ui_manager (editor);
 
        /* Because we are loading from a hard-coded string, there is
         * no chance of I/O errors.  Failure here implies a malformed
@@ -511,103 +514,102 @@ mail_signature_editor_constructed (GObject *object)
                action_group, GETTEXT_PACKAGE);
        gtk_action_group_add_actions (
                action_group, entries,
-               G_N_ELEMENTS (entries), editor);
+               G_N_ELEMENTS (entries), window);
        gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
-       editor->priv->action_group = g_object_ref (action_group);
+       window->priv->action_group = g_object_ref (action_group);
 
        /* Hide page properties because it is not inherited in the mail. */
-       action = gtkhtml_editor_get_action (gtkhtml_editor, "properties-page");
+       action = e_html_editor_get_action (editor, "properties-page");
        gtk_action_set_visible (action, FALSE);
 
-       action = gtkhtml_editor_get_action (
-               gtkhtml_editor, "context-properties-page");
+       action = e_html_editor_get_action (editor, "context-properties-page");
        gtk_action_set_visible (action, FALSE);
 
        gtk_ui_manager_ensure_update (ui_manager);
 
-       gtk_window_set_title (GTK_WINDOW (editor), _("Edit Signature"));
+       gtk_window_set_title (GTK_WINDOW (window), _("Edit Signature"));
+       gtk_window_set_default_size (GTK_WINDOW (window), 600, 440);
 
-       /* Construct the signature name entry. */
+       widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       gtk_container_add (GTK_CONTAINER (window), widget);
+       gtk_widget_show (widget);
+
+       container = widget;
+
+       /* Construct the main menu and toolbar. */
+
+       widget = e_html_editor_get_managed_widget (editor, "/main-menu");
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
 
-       container = gtkhtml_editor->vbox;
+       /* Construct the signature name entry. */
 
        widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
        gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
        gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
-       /* Position 2 should be between the main and style toolbars. */
-       gtk_box_reorder_child (GTK_BOX (container), widget, 2);
        gtk_widget_show (widget);
 
-       container = widget;
+       hbox = widget;
 
        widget = gtk_entry_new ();
-       gtk_box_pack_end (GTK_BOX (container), widget, TRUE, TRUE, 0);
-       editor->priv->entry = widget;  /* not referenced */
+       gtk_box_pack_end (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+       window->priv->entry = widget;  /* not referenced */
        gtk_widget_show (widget);
 
        widget = gtk_label_new_with_mnemonic (_("_Signature Name:"));
-       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), editor->priv->entry);
-       gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), window->priv->entry);
+       gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
        gtk_widget_show (widget);
 
-       g_signal_connect (
-               editor, "delete-event",
-               G_CALLBACK (mail_signature_editor_delete_event_cb), NULL);
-
-       /* Construct the alert bar for errors. */
+       /* Construct the main editing area. */
 
-       container = gtkhtml_editor->vbox;
-
-       widget = e_alert_bar_new ();
-       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
-       /* Position 5 should be between the style toolbar and editing area. */
-       gtk_box_reorder_child (GTK_BOX (container), widget, 5);
-       editor->priv->alert_bar = widget;  /* not referenced */
-       /* EAlertBar controls its own visibility. */
+       widget = GTK_WIDGET (editor);
+       gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+       gtk_widget_show (widget);
 
-       /* Configure an EFocusTracker to manage selection actions.
-        *
-        * XXX GtkhtmlEditor does not manage its own selection actions,
-        *     which is technically a bug but works in our favor here
-        *     because it won't cause any conflicts with EFocusTracker. */
+       g_signal_connect (
+               window, "delete-event",
+               G_CALLBACK (mail_signature_editor_delete_event_cb), NULL);
 
-       focus_tracker = e_focus_tracker_new (GTK_WINDOW (editor));
+       /* Configure an EFocusTracker to manage selection actions. */
+       focus_tracker = e_focus_tracker_new (GTK_WINDOW (window));
 
-       action = gtkhtml_editor_get_action (gtkhtml_editor, "cut");
+       action = e_html_editor_get_action (editor, "cut");
        e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
 
-       action = gtkhtml_editor_get_action (gtkhtml_editor, "copy");
+       action = e_html_editor_get_action (editor, "copy");
        e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
 
-       action = gtkhtml_editor_get_action (gtkhtml_editor, "paste");
+       action = e_html_editor_get_action (editor, "paste");
        e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
 
-       action = gtkhtml_editor_get_action (gtkhtml_editor, "select-all");
+       action = e_html_editor_get_action (editor, "select-all");
        e_focus_tracker_set_select_all_action (focus_tracker, action);
 
-       editor->priv->focus_tracker = focus_tracker;
+       window->priv->focus_tracker = focus_tracker;
 
-       source = e_mail_signature_editor_get_source (editor);
+       source = e_mail_signature_editor_get_source (window);
 
        display_name = e_source_get_display_name (source);
        if (display_name == NULL || *display_name == '\0')
                display_name = _("Unnamed");
 
        /* Set the entry text before we grab focus. */
-       g_free (editor->priv->original_name);
-       editor->priv->original_name = g_strdup (display_name);
-       gtk_entry_set_text (GTK_ENTRY (editor->priv->entry), display_name);
+       g_free (window->priv->original_name);
+       window->priv->original_name = g_strdup (display_name);
+       gtk_entry_set_text (GTK_ENTRY (window->priv->entry), display_name);
 
        /* Set the focus appropriately.  If this is a new signature, draw
         * the user's attention to the signature name entry.  Otherwise go
         * straight to the editing area. */
        if (source == NULL) {
-               gtk_widget_grab_focus (editor->priv->entry);
+               gtk_widget_grab_focus (window->priv->entry);
        } else {
-               GtkHTML *html;
-
-               html = gtkhtml_editor_get_html (gtkhtml_editor);
-               gtk_widget_grab_focus (GTK_WIDGET (html));
+               gtk_widget_grab_focus (GTK_WIDGET (view));
        }
 
        /* Load file content only for an existing signature.
@@ -623,72 +625,19 @@ mail_signature_editor_constructed (GObject *object)
                        G_PRIORITY_DEFAULT,
                        cancellable,
                        mail_signature_editor_loaded_cb,
-                       g_object_ref (editor));
+                       g_object_ref (window));
 
-               g_warn_if_fail (editor->priv->cancellable == NULL);
-               editor->priv->cancellable = cancellable;
+               g_warn_if_fail (window->priv->cancellable == NULL);
+               window->priv->cancellable = cancellable;
 
                g_object_unref (dbus_object);
        }
 }
 
 static void
-mail_signature_editor_cut_clipboard (GtkhtmlEditor *editor)
-{
-       /* Do nothing.  EFocusTracker handles this. */
-}
-
-static void
-mail_signature_editor_copy_clipboard (GtkhtmlEditor *editor)
-{
-       /* Do nothing.  EFocusTracker handles this. */
-}
-
-static void
-mail_signature_editor_paste_clipboard (GtkhtmlEditor *editor)
-{
-       /* Do nothing.  EFocusTracker handles this. */
-}
-
-static void
-mail_signature_editor_select_all (GtkhtmlEditor *editor)
-{
-       /* Do nothing.  EFocusTracker handles this. */
-}
-
-static void
-mail_signature_editor_submit_alert (EAlertSink *alert_sink,
-                                    EAlert *alert)
-{
-       EMailSignatureEditorPrivate *priv;
-       EAlertBar *alert_bar;
-       GtkWidget *dialog;
-       GtkWindow *parent;
-
-       priv = E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE (alert_sink);
-
-       switch (e_alert_get_message_type (alert)) {
-               case GTK_MESSAGE_INFO:
-               case GTK_MESSAGE_WARNING:
-               case GTK_MESSAGE_ERROR:
-                       alert_bar = E_ALERT_BAR (priv->alert_bar);
-                       e_alert_bar_add_alert (alert_bar, alert);
-                       break;
-
-               default:
-                       parent = GTK_WINDOW (alert_sink);
-                       dialog = e_alert_dialog_new (parent, alert);
-                       gtk_dialog_run (GTK_DIALOG (dialog));
-                       gtk_widget_destroy (dialog);
-                       break;
-       }
-}
-
-static void
 e_mail_signature_editor_class_init (EMailSignatureEditorClass *class)
 {
        GObjectClass *object_class;
-       GtkhtmlEditorClass *editor_class;
 
        g_type_class_add_private (class, sizeof (EMailSignatureEditorPrivate));
 
@@ -699,11 +648,16 @@ e_mail_signature_editor_class_init (EMailSignatureEditorClass *class)
        object_class->finalize = mail_signature_editor_finalize;
        object_class->constructed = mail_signature_editor_constructed;
 
-       editor_class = GTKHTML_EDITOR_CLASS (class);
-       editor_class->cut_clipboard = mail_signature_editor_cut_clipboard;
-       editor_class->copy_clipboard = mail_signature_editor_copy_clipboard;
-       editor_class->paste_clipboard = mail_signature_editor_paste_clipboard;
-       editor_class->select_all = mail_signature_editor_select_all;
+       g_object_class_install_property (
+               object_class,
+               PROP_EDITOR,
+               g_param_spec_object (
+                       "editor",
+                       NULL,
+                       NULL,
+                       E_TYPE_HTML_EDITOR,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
 
        g_object_class_install_property (
                object_class,
@@ -742,15 +696,11 @@ e_mail_signature_editor_class_init (EMailSignatureEditorClass *class)
 }
 
 static void
-e_mail_signature_editor_alert_sink_init (EAlertSinkInterface *iface)
-{
-       iface->submit_alert = mail_signature_editor_submit_alert;
-}
-
-static void
 e_mail_signature_editor_init (EMailSignatureEditor *editor)
 {
        editor->priv = E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE (editor);
+
+       editor->priv->editor = g_object_ref_sink (e_html_editor_new ());
 }
 
 GtkWidget *
@@ -764,11 +714,18 @@ e_mail_signature_editor_new (ESourceRegistry *registry,
 
        return g_object_new (
                E_TYPE_MAIL_SIGNATURE_EDITOR,
-               "html", e_web_view_gtkhtml_new (),
                "registry", registry,
                "source", source, NULL);
 }
 
+EHTMLEditor *
+e_mail_signature_editor_get_editor (EMailSignatureEditor *editor)
+{
+       g_return_val_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (editor), NULL);
+
+       return editor->priv->editor;
+}
+
 EFocusTracker *
 e_mail_signature_editor_get_focus_tracker (EMailSignatureEditor *editor)
 {
@@ -851,7 +808,7 @@ mail_signature_editor_commit_cb (GObject *object,
 }
 
 void
-e_mail_signature_editor_commit (EMailSignatureEditor *editor,
+e_mail_signature_editor_commit (EMailSignatureEditor *window,
                                 GCancellable *cancellable,
                                 GAsyncReadyCallback callback,
                                 gpointer user_data)
@@ -864,23 +821,23 @@ e_mail_signature_editor_commit (EMailSignatureEditor *editor,
        const gchar *extension_name;
        const gchar *mime_type;
        gchar *contents;
-       gboolean is_html;
-       gsize length;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
-       g_return_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (editor));
+       g_return_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (window));
 
-       registry = e_mail_signature_editor_get_registry (editor);
-       source = e_mail_signature_editor_get_source (editor);
-       is_html = gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (editor));
+       registry = e_mail_signature_editor_get_registry (window);
+       source = e_mail_signature_editor_get_source (window);
+
+       editor = e_mail_signature_editor_get_editor (window);
+       view = e_html_editor_get_view (editor);
 
-       if (is_html) {
+       if (e_html_editor_view_get_html_mode (view)) {
                mime_type = "text/html";
-               contents = gtkhtml_editor_get_text_html (
-                       GTKHTML_EDITOR (editor), &length);
+               contents = e_html_editor_view_get_text_html (view);
        } else {
                mime_type = "text/plain";
-               contents = gtkhtml_editor_get_text_plain (
-                       GTKHTML_EDITOR (editor), &length);
+               contents = e_html_editor_view_get_text_plain (view);
        }
 
        extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
@@ -890,13 +847,13 @@ e_mail_signature_editor_commit (EMailSignatureEditor *editor,
        async_context = g_slice_new0 (AsyncContext);
        async_context->source = g_object_ref (source);
        async_context->contents = contents;  /* takes ownership */
-       async_context->length = length;
+       async_context->length = strlen (contents);
 
        if (G_IS_CANCELLABLE (cancellable))
                async_context->cancellable = g_object_ref (cancellable);
 
        simple = g_simple_async_result_new (
-               G_OBJECT (editor), callback, user_data,
+               G_OBJECT (window), callback, user_data,
                e_mail_signature_editor_commit);
 
        g_simple_async_result_set_op_res_gpointer (
diff --git a/e-util/e-mail-signature-editor.h b/e-util/e-mail-signature-editor.h
index 7de6841..1b8622d 100644
--- a/e-util/e-mail-signature-editor.h
+++ b/e-util/e-mail-signature-editor.h
@@ -22,9 +22,9 @@
 #ifndef E_MAIL_SIGNATURE_EDITOR_H
 #define E_MAIL_SIGNATURE_EDITOR_H
 
-#include <gtkhtml-editor.h>
 #include <libedataserver/libedataserver.h>
 
+#include <e-util/e-html-editor.h>
 #include <e-util/e-focus-tracker.h>
 
 /* Standard GObject macros */
@@ -53,18 +53,20 @@ typedef struct _EMailSignatureEditorClass EMailSignatureEditorClass;
 typedef struct _EMailSignatureEditorPrivate EMailSignatureEditorPrivate;
 
 struct _EMailSignatureEditor {
-       GtkhtmlEditor parent;
+       GtkWindow parent;
        EMailSignatureEditorPrivate *priv;
 };
 
 struct _EMailSignatureEditorClass {
-       GtkhtmlEditorClass parent_class;
+       GtkWindowClass parent_class;
 };
 
 GType          e_mail_signature_editor_get_type
                                                (void) G_GNUC_CONST;
 GtkWidget *    e_mail_signature_editor_new     (ESourceRegistry *registry,
                                                 ESource *source);
+EHTMLEditor *  e_mail_signature_editor_get_editor
+                                               (EMailSignatureEditor *editor);
 EFocusTracker *        e_mail_signature_editor_get_focus_tracker
                                                (EMailSignatureEditor *editor);
 ESourceRegistry *
diff --git a/e-util/e-mail-signature-manager.c b/e-util/e-mail-signature-manager.c
index 7f75cb7..fb34c7e 100644
--- a/e-util/e-mail-signature-manager.c
+++ b/e-util/e-mail-signature-manager.c
@@ -398,15 +398,22 @@ mail_signature_manager_constructed (GObject *object)
 static void
 mail_signature_manager_add_signature (EMailSignatureManager *manager)
 {
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        ESourceRegistry *registry;
-       GtkWidget *editor;
+       GtkWidget *widget;
 
        registry = e_mail_signature_manager_get_registry (manager);
 
-       editor = e_mail_signature_editor_new (registry, NULL);
-       gtkhtml_editor_set_html_mode (
-               GTKHTML_EDITOR (editor), manager->priv->prefer_html);
-       mail_signature_manager_emit_editor_created (manager, editor);
+       widget = e_mail_signature_editor_new (registry, NULL);
+
+       editor = e_mail_signature_editor_get_editor (
+               E_MAIL_SIGNATURE_EDITOR (widget));
+       view = e_html_editor_get_view (editor);
+       e_html_editor_view_set_html_mode (
+               view, manager->priv->prefer_html);
+
+       mail_signature_manager_emit_editor_created (manager, widget);
 
        gtk_widget_grab_focus (manager->priv->tree_view);
 }
@@ -435,6 +442,7 @@ mail_signature_manager_editor_created (EMailSignatureManager *manager,
 
        gtk_window_set_transient_for (GTK_WINDOW (editor), parent);
        gtk_window_set_position (GTK_WINDOW (editor), position);
+       gtk_widget_set_size_request (GTK_WIDGET (editor), 450, 300);
        gtk_widget_show (GTK_WIDGET (editor));
 }
 
diff --git a/e-util/e-mail-signature-preview.c b/e-util/e-mail-signature-preview.c
index c2eeaa6..ecf6419 100644
--- a/e-util/e-mail-signature-preview.c
+++ b/e-util/e-mail-signature-preview.c
@@ -145,12 +145,16 @@ mail_signature_preview_load_cb (ESource *source,
        mime_type = e_source_mail_signature_get_mime_type (extension);
 
        if (g_strcmp0 (mime_type, "text/html") == 0) {
-               e_web_view_load_string (E_WEB_VIEW (preview), contents);
+               webkit_web_view_load_string (
+                       WEBKIT_WEB_VIEW (preview), contents,
+                       "text/html", "UTF-8", "file:///");
        } else {
                gchar *string;
 
                string = g_markup_printf_escaped ("<pre>%s</pre>", contents);
-               e_web_view_load_string (E_WEB_VIEW (preview), string);
+               webkit_web_view_load_string (
+                       WEBKIT_WEB_VIEW (preview), string,
+                       "text/html", "UTF-8", "file:///");
                g_free (string);
        }
 
diff --git a/e-util/e-misc-utils.c b/e-util/e-misc-utils.c
index 510dad3..88f6da9 100644
--- a/e-util/e-misc-utils.c
+++ b/e-util/e-misc-utils.c
@@ -1089,6 +1089,50 @@ e_str_without_underscores (const gchar *string)
        return new_string;
 }
 
+/**
+ * e_str_replace_string
+ * @text: the string to replace
+ * @before: the string to be replaced
+ * @after: the string to replaced with
+ *
+ * Replaces every occurrence of the string @before with the string @after in
+ * the string @text and returns a #GString with result that should be freed
+ * with g_string_free().
+ *
+ * Returns: a newly-allocated #GString
+ */
+GString *
+e_str_replace_string (const gchar *text,
+                      const gchar *before,
+                      const gchar *after)
+{
+       const gchar *p, *next;
+       GString *str;
+       gint find_len;
+
+       g_return_val_if_fail (text != NULL, NULL);
+       g_return_val_if_fail (before != NULL, NULL);
+       g_return_val_if_fail (*before, NULL);
+
+       find_len = strlen (before);
+       str = g_string_new ("");
+
+       p = text;
+       while (next = strstr (p, before), next) {
+               if (p < next)
+                       g_string_append_len (str, p, next - p);
+
+               if (after && *after)
+                       g_string_append (str, after);
+
+               p = next + find_len;
+       }
+
+       g_string_append (str, p);
+
+       return str;
+}
+
 gint
 e_str_compare (gconstpointer x,
                gconstpointer y)
diff --git a/e-util/e-misc-utils.h b/e-util/e-misc-utils.h
index ebc860b..5810490 100644
--- a/e-util/e-misc-utils.h
+++ b/e-util/e-misc-utils.h
@@ -99,6 +99,9 @@ gchar *               e_ascii_dtostr                  (gchar *buffer,
                                                 gdouble d);
 
 gchar *                e_str_without_underscores       (const gchar *string);
+GString *      e_str_replace_string            (const gchar *text,
+                                                const gchar *find,
+                                                const gchar *replace);
 gint           e_str_compare                   (gconstpointer x,
                                                 gconstpointer y);
 gint           e_str_case_compare              (gconstpointer x,
diff --git a/e-util/e-name-selector-entry.c b/e-util/e-name-selector-entry.c
index a50c886..5a6519c 100644
--- a/e-util/e-name-selector-entry.c
+++ b/e-util/e-name-selector-entry.c
@@ -511,10 +511,10 @@ is_quoted_at (const gchar *string,
                gunichar c = g_utf8_get_char (p);
 
                if (c == '"')
-                       quoted = ~quoted;
+                       quoted = !quoted;
        }
 
-       return quoted ? TRUE : FALSE;
+       return quoted;
 }
 
 static gint
@@ -530,7 +530,7 @@ get_index_at_position (const gchar *string,
                gunichar c = g_utf8_get_char (p);
 
                if (c == '"')
-                       quoted = ~quoted;
+                       quoted = !quoted;
                else if (c == ',' && !quoted)
                        n++;
        }
diff --git a/e-util/e-spell-checker.c b/e-util/e-spell-checker.c
new file mode 100644
index 0000000..c781672
--- /dev/null
+++ b/e-util/e-spell-checker.c
@@ -0,0 +1,783 @@
+/*
+ * e-spell-checker.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-spell-checker.h"
+#include "e-spell-dictionary.h"
+
+#include <libebackend/libebackend.h>
+#include <webkit/webkitspellchecker.h>
+#include <pango/pango.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define E_SPELL_CHECKER_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_SPELL_CHECKER, ESpellCheckerPrivate))
+
+#define MAX_SUGGESTIONS 10
+
+struct _ESpellCheckerPrivate {
+       EnchantBroker *broker;
+       GHashTable *active_dictionaries;
+       GHashTable *dictionaries_cache;
+       gboolean dictionaries_loaded;
+
+       /* We retain ownership of the EnchantDict's since they
+        * have to be freed through enchant_broker_free_dict()
+        * and we also own the EnchantBroker. */
+       GHashTable *enchant_dicts;
+};
+
+enum {
+       PROP_0,
+       PROP_ACTIVE_LANGUAGES
+};
+
+/* Forward Declarations */
+static void    e_spell_checker_init_webkit_checker
+                               (WebKitSpellCheckerInterface *interface);
+
+G_DEFINE_TYPE_EXTENDED (
+       ESpellChecker,
+       e_spell_checker,
+       G_TYPE_OBJECT,
+       0,
+       G_IMPLEMENT_INTERFACE (
+               E_TYPE_EXTENSIBLE, NULL)
+       G_IMPLEMENT_INTERFACE (
+               WEBKIT_TYPE_SPELL_CHECKER,
+               e_spell_checker_init_webkit_checker))
+
+/**
+ * ESpellChecker:
+ *
+ * #ESpellChecker represents a spellchecker in Evolution. It can be used as a
+ * provider for dictionaries. It also implements #WebKitSpellCheckerInterface,
+ * so it can be set as a default spell-checker to WebKit editors
+ */
+
+static gboolean
+spell_checker_enchant_dicts_foreach_cb (gpointer key,
+                                        gpointer value,
+                                        gpointer user_data)
+{
+       EnchantDict *enchant_dict = value;
+       EnchantBroker *enchant_broker = user_data;
+
+       enchant_broker_free_dict (enchant_broker, enchant_dict);
+
+       return TRUE;
+}
+
+static void
+wksc_check_spelling (WebKitSpellChecker *webkit_checker,
+                     const gchar *word,
+                     gint *misspelling_location,
+                     gint *misspelling_length)
+{
+       ESpellChecker *checker = E_SPELL_CHECKER (webkit_checker);
+       GHashTable *active_dictionaries;
+       PangoLanguage *language;
+       PangoLogAttr *attrs;
+       gint length, ii;
+
+       active_dictionaries = checker->priv->active_dictionaries;
+       if (g_hash_table_size (active_dictionaries) == 0)
+               return;
+
+       length = g_utf8_strlen (word, -1);
+
+       language = pango_language_get_default ();
+       attrs = g_new (PangoLogAttr, length + 1);
+
+       pango_get_log_attrs (word, -1, -1, language, attrs, length + 1);
+
+       for (ii = 0; ii < length + 1; ii++) {
+               /* We go through each character until we find an is_word_start,
+                * then we get into an inner loop to find the is_word_end
+                * corresponding */
+               if (attrs[ii].is_word_start) {
+                       gboolean word_recognized;
+                       gint start = ii;
+                       gint end = ii;
+                       gint word_length;
+                       gchar *cstart;
+                       gint bytes;
+                       gchar *new_word;
+
+                       while (attrs[end].is_word_end < 1)
+                               end++;
+
+                       word_length = end - start;
+                       /* Set the iterator to be at the current word
+                        * end, so we don't check characters twice. */
+                       ii = end;
+
+                       cstart = g_utf8_offset_to_pointer (word, start);
+                       bytes = g_utf8_offset_to_pointer (word, end) - cstart;
+                       new_word = g_new0 (gchar, bytes + 1);
+
+                       g_utf8_strncpy (new_word, cstart, word_length);
+
+                       word_recognized = e_spell_checker_check_word (
+                               checker, new_word, strlen (new_word));
+
+                       if (word_recognized) {
+                               if (misspelling_location != NULL)
+                                       *misspelling_location = -1;
+                               if (misspelling_length != NULL)
+                                       *misspelling_length = 0;
+                       } else {
+                               if (misspelling_location != NULL)
+                                       *misspelling_location = start;
+                               if (misspelling_length != NULL)
+                                       *misspelling_length = word_length;
+                       }
+
+                       g_free (new_word);
+               }
+       }
+
+       g_free (attrs);
+}
+
+static gchar **
+wksc_get_guesses (WebKitSpellChecker *webkit_checker,
+                  const gchar *word,
+                  const gchar *context)
+{
+       ESpellChecker *checker = E_SPELL_CHECKER (webkit_checker);
+       GHashTable *active_dictionaries;
+       GList *list, *link;
+       gchar ** guesses;
+       gint ii = 0;
+
+       guesses = g_new0 (gchar *, MAX_SUGGESTIONS + 1);
+
+       active_dictionaries = checker->priv->active_dictionaries;
+       list = g_hash_table_get_keys (active_dictionaries);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               ESpellDictionary *dictionary;
+               GList *suggestions;
+
+               dictionary = E_SPELL_DICTIONARY (link->data);
+               suggestions = e_spell_dictionary_get_suggestions (
+                       dictionary, word, -1);
+
+               while (suggestions != NULL && ii < MAX_SUGGESTIONS) {
+                       guesses[ii++] = suggestions->data;
+                       suggestions->data = NULL;
+
+                       suggestions = g_list_delete_link (
+                               suggestions, suggestions);
+               }
+
+               g_list_free_full (suggestions, (GDestroyNotify) g_free);
+
+               if (ii >= MAX_SUGGESTIONS)
+                       break;
+       }
+
+       g_list_free (list);
+
+       return guesses;
+}
+
+static gchar *
+wksc_get_autocorrect_suggestions (WebKitSpellChecker *webkit_checker,
+                                  const gchar *word)
+{
+       /* Not supported/needed */
+       return NULL;
+}
+
+static void
+spell_checker_learn_word (WebKitSpellChecker *webkit_checker,
+                          const gchar *word)
+{
+       /* Carefully, this will add the word to all active dictionaries! */
+
+       ESpellChecker *checker;
+       GHashTable *active_dictionaries;
+       GList *list, *link;
+
+       checker = E_SPELL_CHECKER (webkit_checker);
+       active_dictionaries = checker->priv->active_dictionaries;
+       list = g_hash_table_get_keys (active_dictionaries);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               ESpellDictionary *dictionary;
+
+               dictionary = E_SPELL_DICTIONARY (link->data);
+               e_spell_dictionary_learn_word (dictionary, word, -1);
+       }
+
+       g_list_free (list);
+}
+
+static void
+spell_checker_ignore_word (WebKitSpellChecker *webkit_checker,
+                           const gchar *word)
+{
+       /* Carefully, this will add the word to all active dictionaries */
+
+       ESpellChecker *checker;
+       GHashTable *active_dictionaries;
+       GList *list, *link;
+
+       checker = E_SPELL_CHECKER (webkit_checker);
+       active_dictionaries = checker->priv->active_dictionaries;
+       list = g_hash_table_get_keys (active_dictionaries);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               ESpellDictionary *dictionary;
+
+               dictionary = E_SPELL_DICTIONARY (link->data);
+               e_spell_dictionary_ignore_word (dictionary, word, -1);
+       }
+
+       g_list_free (list);
+}
+
+static void
+wksc_update_languages (WebKitSpellChecker *webkit_checker,
+                       const gchar *languages)
+{
+       ESpellChecker *checker;
+       GHashTable *active_dictionaries;
+       GQueue queue = G_QUEUE_INIT;
+
+       checker = E_SPELL_CHECKER (webkit_checker);
+       active_dictionaries = checker->priv->active_dictionaries;
+
+       if (languages != NULL) {
+               gchar **langs;
+               gint ii;
+
+               langs = g_strsplit (languages, ",", -1);
+               for (ii = 0; langs[ii] != NULL; ii++) {
+                       ESpellDictionary *dictionary;
+
+                       dictionary = e_spell_checker_ref_dictionary (
+                               checker, langs[ii]);
+                       if (dictionary != NULL)
+                               g_queue_push_tail (&queue, dictionary);
+               }
+               g_strfreev (langs);
+       } else {
+               ESpellDictionary *dictionary;
+               PangoLanguage *pango_language;
+               const gchar *language;
+
+               pango_language = gtk_get_default_language ();
+               language = pango_language_to_string (pango_language);
+               dictionary = e_spell_checker_ref_dictionary (checker, language);
+
+               if (dictionary == NULL) {
+                       GList *list;
+
+                       list = e_spell_checker_list_available_dicts (checker);
+                       if (list != NULL) {
+                               dictionary = g_object_ref (list->data);
+                               g_list_free (list);
+                       }
+               }
+
+               if (dictionary != NULL)
+                       g_queue_push_tail (&queue, dictionary);
+       }
+
+       g_hash_table_remove_all (active_dictionaries);
+
+       while (!g_queue_is_empty (&queue)) {
+               ESpellDictionary *dictionary;
+
+               dictionary = g_queue_pop_head (&queue);
+               g_hash_table_add (active_dictionaries, dictionary);
+       }
+
+       g_object_notify (G_OBJECT (checker), "active-languages");
+}
+
+static void
+spell_checker_get_property (GObject *object,
+                            guint property_id,
+                            GValue *value,
+                            GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_ACTIVE_LANGUAGES:
+                       g_value_take_boxed (
+                               value,
+                               e_spell_checker_list_active_languages (
+                               E_SPELL_CHECKER (object), NULL));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_checker_dispose (GObject *object)
+{
+       ESpellCheckerPrivate *priv;
+
+       priv = E_SPELL_CHECKER_GET_PRIVATE (object);
+
+       g_hash_table_remove_all (priv->active_dictionaries);
+       g_hash_table_remove_all (priv->dictionaries_cache);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_spell_checker_parent_class)->dispose (object);
+}
+
+static void
+spell_checker_finalize (GObject *object)
+{
+       ESpellCheckerPrivate *priv;
+
+       priv = E_SPELL_CHECKER_GET_PRIVATE (object);
+
+       /* Freeing EnchantDicts requires help from EnchantBroker. */
+       g_hash_table_foreach_remove (
+               priv->enchant_dicts,
+               spell_checker_enchant_dicts_foreach_cb,
+               priv->broker);
+       g_hash_table_destroy (priv->enchant_dicts);
+
+       enchant_broker_free (priv->broker);
+
+       g_hash_table_destroy (priv->active_dictionaries);
+       g_hash_table_destroy (priv->dictionaries_cache);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_spell_checker_parent_class)->finalize (object);
+}
+
+static void
+spell_checker_constructed (GObject *object)
+{
+       /* Chain up to parent's constructed() method. */
+       G_OBJECT_CLASS (e_spell_checker_parent_class)->constructed (object);
+
+       e_extensible_load_extensions (E_EXTENSIBLE (object));
+}
+
+static void
+e_spell_checker_class_init (ESpellCheckerClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (ESpellCheckerPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->get_property = spell_checker_get_property;
+       object_class->dispose = spell_checker_dispose;
+       object_class->finalize = spell_checker_finalize;
+       object_class->constructed = spell_checker_constructed;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_ACTIVE_LANGUAGES,
+               g_param_spec_boxed (
+                       "active-languages",
+                       "Active Languages",
+                       "Active spell check language codes",
+                       G_TYPE_STRV,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_spell_checker_init_webkit_checker (WebKitSpellCheckerInterface *interface)
+{
+       interface->check_spelling_of_string = wksc_check_spelling;
+       interface->get_autocorrect_suggestions_for_misspelled_word =
+               wksc_get_autocorrect_suggestions;
+       interface->get_guesses_for_word = wksc_get_guesses;
+       interface->ignore_word = spell_checker_ignore_word;
+       interface->learn_word = spell_checker_learn_word;
+       interface->update_spell_checking_languages = wksc_update_languages;
+}
+
+static void
+e_spell_checker_init (ESpellChecker *checker)
+{
+       GHashTable *active_dictionaries;
+       GHashTable *dictionaries_cache;
+       GHashTable *enchant_dicts;
+
+       active_dictionaries = g_hash_table_new_full (
+               (GHashFunc) e_spell_dictionary_hash,
+               (GEqualFunc) e_spell_dictionary_equal,
+               (GDestroyNotify) g_object_unref,
+               (GDestroyNotify) NULL);
+
+       dictionaries_cache = g_hash_table_new_full (
+               (GHashFunc) g_str_hash,
+               (GEqualFunc) g_str_equal,
+               (GDestroyNotify) NULL,
+               (GDestroyNotify) g_object_unref);
+
+       enchant_dicts = g_hash_table_new_full (
+               (GHashFunc) g_str_hash,
+               (GEqualFunc) g_str_equal,
+               (GDestroyNotify) g_free,
+               (GDestroyNotify) NULL);
+
+       checker->priv = E_SPELL_CHECKER_GET_PRIVATE (checker);
+
+       checker->priv->broker = enchant_broker_init ();
+       checker->priv->active_dictionaries = active_dictionaries;
+       checker->priv->dictionaries_cache = dictionaries_cache;
+       checker->priv->enchant_dicts = enchant_dicts;
+}
+
+/**
+ * e_spell_checker_new:
+ *
+ * Creates a new #ESpellChecker instance.
+ *
+ * Returns: a new #ESpellChecker
+ **/
+ESpellChecker *
+e_spell_checker_new (void)
+{
+       return g_object_new (E_TYPE_SPELL_CHECKER, NULL);
+}
+
+static void
+list_enchant_dicts (const gchar * const lang_tag,
+                    const gchar * const provider_name,
+                    const gchar * const provider_desc,
+                    const gchar * const provider_file,
+                    gpointer user_data)
+{
+       ESpellChecker *checker = user_data;
+       EnchantDict *enchant_dict;
+
+       enchant_dict = enchant_broker_request_dict (
+               checker->priv->broker, lang_tag);
+       if (enchant_dict != NULL) {
+               ESpellDictionary *dictionary;
+               const gchar *code;
+
+               /* Note that we retain ownership of the EnchantDict.
+                * Since EnchantDict is not reference counted, we're
+                * merely loaning the pointer to ESpellDictionary. */
+               dictionary = e_spell_dictionary_new (checker, enchant_dict);
+               code = e_spell_dictionary_get_code (dictionary);
+
+               g_hash_table_insert (
+                       checker->priv->dictionaries_cache,
+                       (gpointer) code, dictionary);
+
+               g_hash_table_insert (
+                       checker->priv->enchant_dicts,
+                       g_strdup (code), enchant_dict);
+       }
+}
+
+/**
+ * e_spell_checker_list_available_dicts:
+ * @checker: An #ESpellChecker
+ *
+ * Returns list of all dictionaries available to the actual
+ * spell-checking backend.
+ *
+ * Returns: new copy of #GList of #ESpellDictionary. The dictionaries are
+ * owned by the @checker and should not be free'd. The list should be freed
+ * using g_list_free() when not neede anymore. [transfer-list]
+ */
+GList *
+e_spell_checker_list_available_dicts (ESpellChecker *checker)
+{
+       GList *list;
+
+       g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL);
+
+       if (!checker->priv->dictionaries_loaded) {
+               enchant_broker_list_dicts (
+                       checker->priv->broker,
+                       list_enchant_dicts, checker);
+               checker->priv->dictionaries_loaded = TRUE;
+       }
+
+       list = g_hash_table_get_values (checker->priv->dictionaries_cache);
+
+       return g_list_sort (list, (GCompareFunc) e_spell_dictionary_compare);
+}
+
+/**
+ * e_spell_checker_ref_dictionary:
+ * @checker: an #ESpellChecker
+ * @language_code: (allow-none): language code of a dictionary, or %NULL
+ *
+ * Tries to find an #ESpellDictionary for given @language_code.
+ * If @language_code is %NULL, the function will return a default
+ * #ESpellDictionary.
+ *
+ * Returns: an #ESpellDictionary for @language_code
+ */
+ESpellDictionary *
+e_spell_checker_ref_dictionary (ESpellChecker *checker,
+                                const gchar *language_code)
+{
+       ESpellDictionary *dictionary;
+       GList *list;
+
+       g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL);
+
+       /* If the cache has not yet been initialized, do so - we will need
+        * it anyway, Otherwise is this call very cheap */
+       list = e_spell_checker_list_available_dicts (checker);
+
+       if (language_code == NULL) {
+               dictionary = (list != NULL) ? list->data : NULL;
+       } else {
+               dictionary = g_hash_table_lookup (
+                       checker->priv->dictionaries_cache,
+                       language_code);
+       }
+
+       if (dictionary != NULL)
+               g_object_ref (dictionary);
+
+       g_list_free (list);
+
+       return dictionary;
+}
+
+/**
+ * e_spell_checker_get_enchant_dict:
+ * @checker: an #ESpellChecker
+ * @language_code: language code of a dictionary, or %NULL
+ *
+ * Returns the #EnchantDict for @language_code, or %NULL if there is none.
+ *
+ * Returns: the #EnchantDict for @language_code, or %NULL
+ **/
+EnchantDict *
+e_spell_checker_get_enchant_dict (ESpellChecker *checker,
+                                  const gchar *language_code)
+{
+       g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL);
+       g_return_val_if_fail (language_code != NULL, NULL);
+
+       return g_hash_table_lookup (
+               checker->priv->enchant_dicts, language_code);
+}
+
+gboolean
+e_spell_checker_get_language_active (ESpellChecker *checker,
+                                     const gchar *language_code)
+{
+       ESpellDictionary *dictionary;
+       GHashTable *active_dictionaries;
+       gboolean active;
+
+       g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), FALSE);
+       g_return_val_if_fail (language_code != NULL, FALSE);
+
+       dictionary = e_spell_checker_ref_dictionary (checker, language_code);
+       g_return_val_if_fail (dictionary != NULL, FALSE);
+
+       active_dictionaries = checker->priv->active_dictionaries;
+       active = g_hash_table_contains (active_dictionaries, dictionary);
+
+       g_object_unref (dictionary);
+
+       return active;
+}
+
+void
+e_spell_checker_set_language_active (ESpellChecker *checker,
+                                     const gchar *language_code,
+                                     gboolean active)
+{
+       ESpellDictionary *dictionary;
+       GHashTable *active_dictionaries;
+       gboolean is_active;
+
+       g_return_if_fail (E_IS_SPELL_CHECKER (checker));
+       g_return_if_fail (language_code != NULL);
+
+       dictionary = e_spell_checker_ref_dictionary (checker, language_code);
+       g_return_if_fail (dictionary != NULL);
+
+       active_dictionaries = checker->priv->active_dictionaries;
+       is_active = g_hash_table_contains (active_dictionaries, dictionary);
+
+       if (active && !is_active) {
+               g_object_ref (dictionary);
+               g_hash_table_add (active_dictionaries, dictionary);
+               g_object_notify (G_OBJECT (checker), "active-languages");
+       } else if (!active && is_active) {
+               g_hash_table_remove (active_dictionaries, dictionary);
+               g_object_notify (G_OBJECT (checker), "active-languages");
+       }
+
+       g_object_unref (dictionary);
+}
+
+/**
+ * e_spell_checker_list_active_languages:
+ * @checker: an #ESpellChecker
+ * @n_languages: return location for the number of active languages, or %NULL
+ *
+ * Returns a %NULL-terminated array of language codes actively being used
+ * for spell checking.  Free the returned array with g_strfreev().
+ *
+ * Returns: a %NULL-teriminated array of language codes
+ **/
+gchar **
+e_spell_checker_list_active_languages (ESpellChecker *checker,
+                                       guint *n_languages)
+{
+       GHashTable *active_dictionaries;
+       GList *list, *link;
+       gchar **active_languages;
+       guint size;
+       gint ii = 0;
+
+       g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL);
+
+       active_dictionaries = checker->priv->active_dictionaries;
+       list = g_hash_table_get_keys (active_dictionaries);
+       size = g_hash_table_size (active_dictionaries);
+
+       active_languages = g_new0 (gchar *, size + 1);
+
+       list = g_list_sort (list, (GCompareFunc) e_spell_dictionary_compare);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               ESpellDictionary *dictionary;
+               const gchar *language_code;
+
+               dictionary = E_SPELL_DICTIONARY (link->data);
+               language_code = e_spell_dictionary_get_code (dictionary);
+               active_languages[ii++] = g_strdup (language_code);
+       }
+
+       if (n_languages != NULL)
+               *n_languages = size;
+
+       g_list_free (list);
+
+       return active_languages;
+}
+
+/**
+ * e_spell_checker_count_active_languages:
+ * @checker: an #ESpellChecker
+ *
+ * Returns the number of languages actively being used for spell checking.
+ *
+ * Returns: number of active spell checking languages
+ **/
+guint
+e_spell_checker_count_active_languages (ESpellChecker *checker)
+{
+       g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), 0);
+
+       return g_hash_table_size (checker->priv->active_dictionaries);
+}
+
+/**
+ * e_spell_checker_check_word:
+ * @checker: an #SpellChecker
+ * @word: a word to spell-check
+ * @length: length of @word in bytes or -1 when %NULL-terminated
+ *
+ * Calls e_spell_dictionary_check_word() on all active dictionaries in
+ * @checker, and returns %TRUE if @word is recognized by any of them.
+ *
+ * Returns: %TRUE if @word is recognized, %FALSE otherwise
+ **/
+gboolean
+e_spell_checker_check_word (ESpellChecker *checker,
+                            const gchar *word,
+                            gsize length)
+{
+       GList *list, *link;
+       gboolean recognized = FALSE;
+
+       g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), TRUE);
+       g_return_val_if_fail (word != NULL && *word != '\0', TRUE);
+
+       list = g_hash_table_get_keys (checker->priv->active_dictionaries);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               ESpellDictionary *dictionary;
+
+               dictionary = E_SPELL_DICTIONARY (link->data);
+               if (e_spell_dictionary_check_word (dictionary, word, length)) {
+                       recognized = TRUE;
+                       break;
+               }
+       }
+
+       g_list_free (list);
+
+       return recognized;
+}
+
+/**
+ * e_spell_checker_ignore_word:
+ * @checker: an #ESpellChecker
+ * @word: word to ignore for the rest of session
+ *
+ * Calls e_spell_dictionary_ignore_word() on all active dictionaries in
+ * the @checker.
+ */
+void
+e_spell_checker_ignore_word (ESpellChecker *checker,
+                             const gchar *word)
+{
+       WebKitSpellCheckerInterface *interface;
+
+       g_return_if_fail (E_IS_SPELL_CHECKER (checker));
+
+       interface = WEBKIT_SPELL_CHECKER_GET_IFACE (checker);
+       interface->ignore_word (WEBKIT_SPELL_CHECKER (checker), word);
+}
+
+/**
+ * e_spell_checker_learn_word:
+ * @checker: an #ESpellChecker
+ * @word: word to learn
+ *
+ * Calls e_spell_dictionary_learn_word() on all active dictionaries in
+ * the @checker.
+ */
+void
+e_spell_checker_learn_word (ESpellChecker *checker,
+                            const gchar *word)
+{
+       WebKitSpellCheckerInterface *interface;
+
+       g_return_if_fail (E_IS_SPELL_CHECKER (checker));
+
+       interface = WEBKIT_SPELL_CHECKER_GET_IFACE (checker);
+       interface->learn_word (WEBKIT_SPELL_CHECKER (checker), word);
+}
diff --git a/e-util/e-spell-checker.h b/e-util/e-spell-checker.h
new file mode 100644
index 0000000..48303d6
--- /dev/null
+++ b/e-util/e-spell-checker.h
@@ -0,0 +1,95 @@
+/*
+ * e-spell-checker.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_SPELL_CHECKER_H
+#define E_SPELL_CHECKER_H
+
+#include <glib-object.h>
+#include <e-util/e-spell-dictionary.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SPELL_CHECKER \
+       (e_spell_checker_get_type ())
+#define E_SPELL_CHECKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SPELL_CHECKER, ESpellChecker))
+#define E_SPELL_CHECKER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SPELL_CHECKER, ESpellCheckerClass))
+#define E_IS_SPELL_CHECKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SPELL_CHECKER))
+#define E_IS_SPELL_CHECKER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SPELL_CHECKER))
+#define E_SPELL_CHECKER_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SPELL_CHECKER, ESpellCheckerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESpellChecker ESpellChecker;
+typedef struct _ESpellCheckerPrivate ESpellCheckerPrivate;
+typedef struct _ESpellCheckerClass ESpellCheckerClass;
+
+struct _ESpellChecker {
+       GObject parent;
+       ESpellCheckerPrivate *priv;
+};
+
+struct _ESpellCheckerClass {
+       GObjectClass parent_class;
+};
+
+GType          e_spell_checker_get_type        (void) G_GNUC_CONST;
+ESpellChecker *        e_spell_checker_new             (void);
+GList *                e_spell_checker_list_available_dicts
+                                               (ESpellChecker *checker);
+ESpellDictionary *
+               e_spell_checker_ref_dictionary  (ESpellChecker *checker,
+                                                const gchar *language_code);
+EnchantDict *  e_spell_checker_get_enchant_dict
+                                               (ESpellChecker *checker,
+                                                const gchar *language_code);
+gboolean       e_spell_checker_get_language_active
+                                               (ESpellChecker *checker,
+                                                const gchar *language_code);
+void           e_spell_checker_set_language_active
+                                               (ESpellChecker *checker,
+                                                const gchar *language_code,
+                                                gboolean active);
+gchar **       e_spell_checker_list_active_languages
+                                               (ESpellChecker *checker,
+                                                guint *n_languages);
+guint          e_spell_checker_count_active_languages
+                                               (ESpellChecker *checker);
+gboolean       e_spell_checker_check_word      (ESpellChecker *checker,
+                                                const gchar *word,
+                                                gsize length);
+void           e_spell_checker_learn_word      (ESpellChecker *checker,
+                                                const gchar *word);
+void           e_spell_checker_ignore_word     (ESpellChecker *checker,
+                                                const gchar *word);
+
+G_END_DECLS
+
+#endif /* E_SPELL_CHECKER_H */
diff --git a/e-util/e-spell-dictionary.c b/e-util/e-spell-dictionary.c
new file mode 100644
index 0000000..e6e06b7
--- /dev/null
+++ b/e-util/e-spell-dictionary.c
@@ -0,0 +1,797 @@
+/*
+ * e-spell-dictionary.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-spell-dictionary.h"
+#include "e-spell-checker.h"
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+
+#define E_SPELL_DICTIONARY_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_SPELL_DICTIONARY, ESpellDictionaryPrivate))
+
+/**
+ * ESpellDictionary:
+ *
+ * The #ESpellDictionary is a wrapper around #EnchantDict.
+ */
+
+enum {
+       PROP_0,
+       PROP_SPELL_CHECKER
+};
+
+struct _ESpellDictionaryPrivate {
+       GWeakRef spell_checker;
+
+       gchar *name;
+       gchar *code;
+       gchar *collate_key;
+};
+
+#define ISO_639_DOMAIN "iso_639"
+#define ISO_3166_DOMAIN        "iso_3166"
+
+static GHashTable *iso_639_table = NULL;
+static GHashTable *iso_3166_table = NULL;
+
+G_DEFINE_TYPE (
+       ESpellDictionary,
+       e_spell_dictionary,
+       G_TYPE_OBJECT);
+
+#ifdef HAVE_ISO_CODES
+
+#define ISOCODESLOCALEDIR ISO_CODES_PREFIX "/share/locale"
+
+#ifdef G_OS_WIN32
+#ifdef DATADIR
+#undef DATADIR
+#endif
+#include <shlobj.h>
+static HMODULE hmodule;
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+         DWORD fdwReason,
+         LPVOID lpvReserved);
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+         DWORD fdwReason,
+         LPVOID lpvReserved)
+{
+       switch (fdwReason)
+    {
+    case DLL_PROCESS_ATTACH:
+               hmodule = hinstDLL;
+               break;
+    }
+
+       return TRUE;
+}
+
+static gchar *
+_get_iso_codes_prefix (void)
+{
+       static gchar retval[1000];
+       static gint beenhere = 0;
+       gchar *temp_dir = 0;
+
+       if (beenhere)
+               return retval;
+
+       if (!(temp_dir = g_win32_get_package_installation_directory_of_module ((gpointer) hmodule))) {
+               strcpy (retval, ISO_CODES_PREFIX);
+               return retval;
+       }
+
+       strcpy (retval, temp_dir);
+       g_free (temp_dir);
+       beenhere = 1;
+       return retval;
+}
+
+static gchar *
+_get_isocodeslocaledir (void)
+{
+       static gchar retval[1000];
+       static gint beenhere = 0;
+
+       if (beenhere)
+               return retval;
+
+       strcpy (retval, _get_iso_codes_prefix ());
+       strcat (retval, "\\share\\locale" );
+       beenhere = 1;
+       return retval;
+}
+
+#undef ISO_CODES_PREFIX
+#define ISO_CODES_PREFIX _get_iso_codes_prefix ()
+
+#undef ISOCODESLOCALEDIR
+#define ISOCODESLOCALEDIR _get_isocodeslocaledir ()
+
+#endif
+
+static void
+iso_639_start_element (GMarkupParseContext *context,
+                       const gchar *element_name,
+                       const gchar **attribute_names,
+                       const gchar **attribute_values,
+                       gpointer data,
+                       GError **error)
+{
+       GHashTable *hash_table = data;
+       const gchar *iso_639_1_code = NULL;
+       const gchar *iso_639_2_code = NULL;
+       const gchar *name = NULL;
+       const gchar *code = NULL;
+       gint ii;
+
+       if (g_strcmp0 (element_name, "iso_639_entry") != 0) {
+               return;
+       }
+
+       for (ii = 0; attribute_names[ii] != NULL; ii++) {
+               if (strcmp (attribute_names[ii], "name") == 0)
+                       name = attribute_values[ii];
+               else if (strcmp (attribute_names[ii], "iso_639_1_code") == 0)
+                       iso_639_1_code = attribute_values[ii];
+               else if (strcmp (attribute_names[ii], "iso_639_2T_code") == 0)
+                       iso_639_2_code = attribute_values[ii];
+       }
+
+       code = (iso_639_1_code != NULL) ? iso_639_1_code : iso_639_2_code;
+
+       if (code != NULL && *code != '\0' && name != NULL && *name != '\0')
+               g_hash_table_insert (
+                       hash_table, g_strdup (code),
+                       g_strdup (dgettext (ISO_639_DOMAIN, name)));
+}
+
+static void
+iso_3166_start_element (GMarkupParseContext *context,
+                        const gchar *element_name,
+                        const gchar **attribute_names,
+                        const gchar **attribute_values,
+                        gpointer data,
+                        GError **error)
+{
+       GHashTable *hash_table = data;
+       const gchar *name = NULL;
+       const gchar *code = NULL;
+       gint ii;
+
+       if (strcmp (element_name, "iso_3166_entry") != 0)
+               return;
+
+       for (ii = 0; attribute_names[ii] != NULL; ii++) {
+               if (strcmp (attribute_names[ii], "name") == 0)
+                       name = attribute_values[ii];
+               else if (strcmp (attribute_names[ii], "alpha_2_code") == 0)
+                       code = attribute_values[ii];
+       }
+
+       if (code != NULL && *code != '\0' && name != NULL && *name != '\0')
+               g_hash_table_insert (
+                       hash_table, g_ascii_strdown (code, -1),
+                       g_strdup (dgettext (ISO_3166_DOMAIN, name)));
+}
+
+static GMarkupParser iso_639_parser = {
+       iso_639_start_element,
+       NULL, NULL, NULL, NULL
+};
+
+static GMarkupParser iso_3166_parser = {
+       iso_3166_start_element,
+       NULL, NULL, NULL, NULL
+};
+
+static void
+iso_codes_parse (const GMarkupParser *parser,
+                 const gchar *basename,
+                 GHashTable *hash_table)
+{
+       GMappedFile *mapped_file;
+       gchar *filename;
+       GError *error = NULL;
+
+       filename = g_build_filename (
+               ISO_CODES_PREFIX, "share", "xml",
+               "iso-codes", basename, NULL);
+       mapped_file = g_mapped_file_new (filename, FALSE, &error);
+       g_free (filename);
+
+       if (mapped_file != NULL) {
+               GMarkupParseContext *context;
+               const gchar *contents;
+               gsize length;
+
+               context = g_markup_parse_context_new (
+                       parser, 0, hash_table, NULL);
+               contents = g_mapped_file_get_contents (mapped_file);
+               length = g_mapped_file_get_length (mapped_file);
+               g_markup_parse_context_parse (
+                       context, contents, length, &error);
+               g_markup_parse_context_free (context);
+#if GLIB_CHECK_VERSION(2,21,3)
+               g_mapped_file_unref (mapped_file);
+#else
+               g_mapped_file_free (mapped_file);
+#endif
+       }
+
+       if (error != NULL) {
+               g_warning ("%s: %s", basename, error->message);
+               g_error_free (error);
+       }
+}
+
+#endif /* HAVE_ISO_CODES */
+
+struct _enchant_dict_description_data {
+       gchar *language_tag;
+       gchar *dict_name;
+};
+
+static void
+describe_dictionary (const gchar *language_tag,
+                     const gchar *provider_name,
+                     const gchar *provider_desc,
+                     const gchar *provider_file,
+                     gpointer user_data)
+{
+       struct _enchant_dict_description_data *data = user_data;
+       const gchar *iso_639_name;
+       const gchar *iso_3166_name;
+       gchar *language_name;
+       gchar *lowercase;
+       gchar **tokens;
+
+       /* Split language code into lowercase tokens. */
+       lowercase = g_ascii_strdown (language_tag, -1);
+       tokens = g_strsplit (lowercase, "_", -1);
+       g_free (lowercase);
+
+       g_return_if_fail (tokens != NULL);
+
+       iso_639_name = g_hash_table_lookup (iso_639_table, tokens[0]);
+
+       if (iso_639_name == NULL) {
+               language_name = g_strdup_printf (
+               /* Translators: %s is the language ISO code. */
+                       C_("language", "Unknown (%s)"), language_tag);
+               goto exit;
+       }
+
+       if (g_strv_length (tokens) < 2) {
+               language_name = g_strdup (iso_639_name);
+               goto exit;
+       }
+
+       iso_3166_name = g_hash_table_lookup (iso_3166_table, tokens[1]);
+
+       if (iso_3166_name != NULL)
+               language_name = g_strdup_printf (
+                /* Translators: The first %s is the language name, and the
+                * second is the country name. Example: "French (France)" */
+                       C_("language", "%s (%s)"), iso_639_name, iso_3166_name);
+       else
+               language_name = g_strdup_printf (
+                /* Translators: The first %s is the language name, and the
+                * second is the country name. Example: "French (France)" */
+                       C_("language", "%s (%s)"), iso_639_name, tokens[1]);
+
+exit:
+       g_strfreev (tokens);
+
+       data->language_tag = g_strdup (language_tag);
+       data->dict_name = language_name;
+}
+
+static void
+spell_dictionary_set_enchant_dict (ESpellDictionary *dictionary,
+                                   EnchantDict *enchant_dict)
+{
+       struct _enchant_dict_description_data data;
+
+       enchant_dict_describe (enchant_dict, describe_dictionary, &data);
+
+       dictionary->priv->code = data.language_tag;
+       dictionary->priv->name = data.dict_name;
+       dictionary->priv->collate_key = g_utf8_collate_key (data.dict_name, -1);
+}
+
+static void
+spell_dictionary_set_spell_checker (ESpellDictionary *dictionary,
+                                    ESpellChecker *spell_checker)
+{
+       g_return_if_fail (E_IS_SPELL_CHECKER (spell_checker));
+
+       g_weak_ref_set (&dictionary->priv->spell_checker, spell_checker);
+}
+
+static void
+spell_dictionary_set_property (GObject *object,
+                               guint property_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_SPELL_CHECKER:
+                       spell_dictionary_set_spell_checker (
+                               E_SPELL_DICTIONARY (object),
+                               g_value_get_object (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_dictionary_get_property (GObject *object,
+                               guint property_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_SPELL_CHECKER:
+                       g_value_take_object (
+                               value,
+                               e_spell_dictionary_ref_spell_checker (
+                               E_SPELL_DICTIONARY (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_dictionary_dispose (GObject *object)
+{
+       ESpellDictionaryPrivate *priv;
+
+       priv = E_SPELL_DICTIONARY_GET_PRIVATE (object);
+
+       g_weak_ref_set (&priv->spell_checker, NULL);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_spell_dictionary_parent_class)->dispose (object);
+}
+
+static void
+spell_dictionary_finalize (GObject *object)
+{
+       ESpellDictionaryPrivate *priv;
+
+       priv = E_SPELL_DICTIONARY_GET_PRIVATE (object);
+
+       g_free (priv->name);
+       g_free (priv->code);
+       g_free (priv->collate_key);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_spell_dictionary_parent_class)->finalize (object);
+}
+
+static void
+e_spell_dictionary_class_init (ESpellDictionaryClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (ESpellDictionaryPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = spell_dictionary_set_property;
+       object_class->get_property = spell_dictionary_get_property;
+       object_class->dispose = spell_dictionary_dispose;
+       object_class->finalize = spell_dictionary_finalize;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_SPELL_CHECKER,
+               g_param_spec_object (
+                       "spell-checker",
+                       NULL,
+                       "Parent spell checker",
+                       E_TYPE_SPELL_CHECKER,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+e_spell_dictionary_init (ESpellDictionary *dictionary)
+{
+       dictionary->priv = E_SPELL_DICTIONARY_GET_PRIVATE (dictionary);
+
+       if (!iso_639_table && !iso_3166_table) {
+#if defined (ENABLE_NLS) && defined (HAVE_ISO_CODES)
+               bindtextdomain (ISO_639_DOMAIN, ISOCODESLOCALEDIR);
+               bind_textdomain_codeset (ISO_639_DOMAIN, "UTF-8");
+
+               bindtextdomain (ISO_3166_DOMAIN, ISOCODESLOCALEDIR);
+               bind_textdomain_codeset (ISO_3166_DOMAIN, "UTF-8");
+#endif /* ENABLE_NLS && HAVE_ISO_CODES */
+
+               iso_639_table = g_hash_table_new_full (
+                       (GHashFunc) g_str_hash,
+                       (GEqualFunc) g_str_equal,
+                       (GDestroyNotify) g_free,
+                       (GDestroyNotify) g_free);
+
+               iso_3166_table = g_hash_table_new_full (
+                       (GHashFunc) g_str_hash,
+                       (GEqualFunc) g_str_equal,
+                       (GDestroyNotify) g_free,
+                       (GDestroyNotify) g_free);
+
+#ifdef HAVE_ISO_CODES
+               iso_codes_parse (
+                       &iso_639_parser, "iso_639.xml", iso_639_table);
+               iso_codes_parse (
+                       &iso_3166_parser, "iso_3166.xml", iso_3166_table);
+#endif /* HAVE_ISO_CODES */
+       }
+}
+
+ESpellDictionary *
+e_spell_dictionary_new (ESpellChecker *spell_checker,
+                        EnchantDict *enchant_dict)
+{
+       ESpellDictionary *dictionary;
+
+       g_return_val_if_fail (E_IS_SPELL_CHECKER (spell_checker), NULL);
+       g_return_val_if_fail (enchant_dict != NULL, NULL);
+
+       dictionary = g_object_new (
+               E_TYPE_SPELL_DICTIONARY,
+               "spell-checker", spell_checker, NULL);
+
+       /* Since EnchantDict is not reference counted, ESpellChecker
+        * is loaning us the EnchantDict pointer.  We do not own it. */
+       spell_dictionary_set_enchant_dict (dictionary, enchant_dict);
+
+       return dictionary;
+}
+
+/**
+ * e_spell_dictionary_hash:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Generates a hash value for @dictionary based on its ISO code.
+ * This function is intended for easily hashing an #ESpellDictionary
+ * to add to a #GHashTable or similar data structure.
+ *
+ * Returns: a hash value for @dictionary
+ **/
+guint
+e_spell_dictionary_hash (ESpellDictionary *dictionary)
+{
+       const gchar *code;
+
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), 0);
+
+       code = e_spell_dictionary_get_code (dictionary);
+
+       return g_str_hash (code);
+}
+
+/**
+ * e_spell_dictionary_equal:
+ * @dictionary1: an #ESpellDictionary
+ * @dictionary2: another #ESpellDictionary
+ *
+ * Checks two #ESpellDictionary instances for equality based on their
+ * ISO codes.
+ *
+ * Returns: %TRUE if @dictionary1 and @dictionary2 are equal
+ **/
+gboolean
+e_spell_dictionary_equal (ESpellDictionary *dictionary1,
+                          ESpellDictionary *dictionary2)
+{
+       const gchar *code1, *code2;
+
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary1), FALSE);
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary2), FALSE);
+
+       if (dictionary1 == dictionary2)
+               return TRUE;
+
+       code1 = e_spell_dictionary_get_code (dictionary1);
+       code2 = e_spell_dictionary_get_code (dictionary2);
+
+       return g_str_equal (code1, code2);
+}
+
+/**
+ * e_spell_dictionary_compare:
+ * @dictionary1: an #ESpellDictionary
+ * @dictionary2: another #ESpellDictionary
+ *
+ * Compares @dictionary1 and @dictionary2 by their display names for
+ * the purpose of lexicographical sorting.  Use this function where a
+ * #GCompareFunc callback is required, such as g_list_sort().
+ *
+ * Returns: 0 if the names match,
+ *          a negative value if @dictionary1 < @dictionary2,
+ *          or a positive value of @dictionary1 > @dictionary2
+ **/
+gint
+e_spell_dictionary_compare (ESpellDictionary *dictionary1,
+                            ESpellDictionary *dictionary2)
+{
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary1), 0);
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary2), 0);
+
+       return strcmp (
+               dictionary1->priv->collate_key,
+               dictionary2->priv->collate_key);
+}
+
+/**
+ * e_spell_dictionary_get_name:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Returns the display name of the dictionary (for example
+ * "English (British)")
+ *
+ * Returns: the display name of the @dictionary
+ */
+const gchar *
+e_spell_dictionary_get_name (ESpellDictionary *dictionary)
+{
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+
+       return dictionary->priv->name;
+}
+
+/**
+ * e_spell_dictionary_get_code:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Returns the ISO code of the spell-checking language for
+ * @dictionary (for example "en_US").
+ *
+ * Returns: the language code of the @dictionary
+ */
+const gchar *
+e_spell_dictionary_get_code (ESpellDictionary *dictionary)
+{
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+
+       return dictionary->priv->code;
+}
+
+/**
+ * e_spell_dictionary_ref_spell_checker:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Returns a new reference to the #ESpellChecker which owns the dictionary.
+ * Unreference the #ESpellChecker with g_object_unref() when finished with it.
+ *
+ * Returns: an #ESpellChecker
+ **/
+ESpellChecker *
+e_spell_dictionary_ref_spell_checker (ESpellDictionary *dictionary)
+{
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+
+       return g_weak_ref_get (&dictionary->priv->spell_checker);
+}
+
+/**
+ * e_spell_dictionary_check_word:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word to spell-check
+ * @length: length of @word in bytes or -1 when %NULL-terminated
+ *
+ * Tries to lookup the @word in the @dictionary to check whether
+ * it's spelled correctly or not.
+ *
+ * Returns: %TRUE if @word is recognized, %FALSE otherwise
+ */
+gboolean
+e_spell_dictionary_check_word (ESpellDictionary *dictionary,
+                               const gchar *word,
+                               gsize length)
+{
+       ESpellChecker *spell_checker;
+       EnchantDict *enchant_dict;
+       gboolean recognized;
+
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), TRUE);
+       g_return_val_if_fail (word != NULL && *word != '\0', TRUE);
+
+       spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+       g_return_val_if_fail (spell_checker != NULL, TRUE);
+
+       enchant_dict = e_spell_checker_get_enchant_dict (
+               spell_checker, e_spell_dictionary_get_code (dictionary));
+       g_return_val_if_fail (enchant_dict != NULL, TRUE);
+
+       recognized = (enchant_dict_check (enchant_dict, word, length) == 0);
+
+       g_object_unref (spell_checker);
+
+       return recognized;
+}
+
+/**
+ * e_spell_dictionary_learn_word:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word to add to @dictionary
+ * @length: length of @word in bytes or -1 when %NULL-terminated
+ *
+ * Permanently adds @word to @dictionary so that next time calling
+ * e_spell_dictionary_check() on the @word will return %TRUE.
+ */
+void
+e_spell_dictionary_learn_word (ESpellDictionary *dictionary,
+                               const gchar *word,
+                               gsize length)
+{
+       ESpellChecker *spell_checker;
+       EnchantDict *enchant_dict;
+
+       g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
+       g_return_if_fail (word != NULL && *word != '\0');
+
+       spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+       g_return_if_fail (spell_checker != NULL);
+
+       enchant_dict = e_spell_checker_get_enchant_dict (
+               spell_checker, e_spell_dictionary_get_code (dictionary));
+       g_return_if_fail (enchant_dict != NULL);
+
+       enchant_dict_add_to_personal (enchant_dict, word, length);
+
+       g_object_unref (spell_checker);
+}
+
+/**
+ * e_spell_dictionary_ignore_word:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word to add to ignore list
+ * @length: length of @word in bytes or -1 when %NULL-terminated
+ *
+ * Adds @word to temporary ignore list of the @dictionary, so that
+ * e_spell_dictionary_check() on the @word will return %TRUE. The
+ * list is cleared when the dictionary is freed.
+ */
+void
+e_spell_dictionary_ignore_word (ESpellDictionary *dictionary,
+                                const gchar *word,
+                                gsize length)
+{
+       ESpellChecker *spell_checker;
+       EnchantDict *enchant_dict;
+
+       g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
+       g_return_if_fail (word != NULL && *word != '\0');
+
+       spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+       g_return_if_fail (spell_checker != NULL);
+
+       enchant_dict = e_spell_checker_get_enchant_dict (
+               spell_checker, e_spell_dictionary_get_code (dictionary));
+       g_return_if_fail (enchant_dict != NULL);
+
+       enchant_dict_add_to_session (enchant_dict, word, length);
+
+       g_object_unref (spell_checker);
+}
+
+/**
+ * e_spell_dictionary_get_suggestions:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word to which to find suggestions
+ * @length: length of @word in bytes or -1 when %NULL-terminated
+ *
+ * Provides list of alternative spellings of @word.
+ *
+ * Free the returned spelling suggestions with g_free(), and the list
+ * itself with g_list_free().  An easy way to free the list properly in
+ * one step is as follows:
+ *
+ * |[
+ *   g_list_free_full (list, (GDestroyNotify) g_free);
+ * ]|
+ *
+ * Returns: a list of spelling suggestions for @word
+ */
+GList *
+e_spell_dictionary_get_suggestions (ESpellDictionary *dictionary,
+                                    const gchar *word,
+                                    gsize length)
+{
+       ESpellChecker *spell_checker;
+       EnchantDict *enchant_dict;
+       GList *list = NULL;
+       gchar **suggestions;
+       gsize ii, count = 0;
+
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+       g_return_val_if_fail (word != NULL && *word != '\0', NULL);
+
+       spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+       g_return_val_if_fail (spell_checker != NULL, NULL);
+
+       enchant_dict = e_spell_checker_get_enchant_dict (
+               spell_checker, e_spell_dictionary_get_code (dictionary));
+       g_return_val_if_fail (enchant_dict != NULL, NULL);
+
+       suggestions = enchant_dict_suggest (enchant_dict, word, length, &count);
+       for (ii = 0; ii < count; ii++)
+               list = g_list_prepend (list, g_strdup (suggestions[ii]));
+       enchant_dict_free_suggestions (enchant_dict, suggestions);
+
+       g_object_unref (spell_checker);
+
+       return g_list_reverse (list);
+}
+
+/**
+ * e_spell_dictionary_add_correction
+ * @dictionary: an #ESpellDictionary
+ * @misspelled: a misspelled word
+ * @misspelled_length: length of @misspelled in bytes or -1 when
+ *                     %NULL-terminated
+ * @correction: the corrected word
+ * @correction_length: length of @correction in bytes or -1 when
+ *                     %NULL-terminated
+ *
+ * Learns a new @correction of @misspelled word.
+ */
+void
+e_spell_dictionary_store_correction (ESpellDictionary *dictionary,
+                                     const gchar *misspelled,
+                                     gsize misspelled_length,
+                                     const gchar *correction,
+                                     gsize correction_length)
+{
+       ESpellChecker *spell_checker;
+       EnchantDict *enchant_dict;
+
+       g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
+       g_return_if_fail (misspelled != NULL && *misspelled != '\0');
+       g_return_if_fail (correction != NULL && *correction != '\0');
+
+       spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+       g_return_if_fail (spell_checker != NULL);
+
+       enchant_dict = e_spell_checker_get_enchant_dict (
+               spell_checker, e_spell_dictionary_get_code (dictionary));
+       g_return_if_fail (enchant_dict != NULL);
+
+       enchant_dict_store_replacement (
+               enchant_dict,
+               misspelled, misspelled_length,
+               correction, correction_length);
+
+       g_object_unref (spell_checker);
+}
+
diff --git a/e-util/e-spell-dictionary.h b/e-util/e-spell-dictionary.h
new file mode 100644
index 0000000..f36bfb4
--- /dev/null
+++ b/e-util/e-spell-dictionary.h
@@ -0,0 +1,99 @@
+/*
+ * e-spell-dictionary.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_SPELL_DICTIONARY_H
+#define E_SPELL_DICTIONARY_H
+
+#include <glib-object.h>
+#include <enchant/enchant.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SPELL_DICTIONARY \
+       (e_spell_dictionary_get_type ())
+#define E_SPELL_DICTIONARY(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SPELL_DICTIONARY, ESpellDictionary))
+#define E_SPELL_DICTIONARY_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SPELL_DICTIONARY, ESpellDictionaryClass))
+#define E_IS_SPELL_DICTIONARY(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SPELL_DICTIONARY))
+#define E_IS_SPELL_DICTIONARY_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SPELL_DICTIONARY))
+#define E_SPELL_DICTIONARY_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SPELL_DICTIONARY, ESpellDictionaryClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESpellDictionary ESpellDictionary;
+typedef struct _ESpellDictionaryPrivate ESpellDictionaryPrivate;
+typedef struct _ESpellDictionaryClass ESpellDictionaryClass;
+typedef struct _ESpellChecker ESpellChecker;
+
+struct _ESpellDictionary {
+       GObject parent;
+       ESpellDictionaryPrivate *priv;
+};
+
+struct _ESpellDictionaryClass {
+       GObjectClass parent_class;
+};
+
+GType          e_spell_dictionary_get_type     (void) G_GNUC_CONST;
+ESpellDictionary *
+               e_spell_dictionary_new          (ESpellChecker *spell_checker,
+                                                EnchantDict *enchant_dict);
+guint          e_spell_dictionary_hash         (ESpellDictionary *dictionary);
+gboolean       e_spell_dictionary_equal        (ESpellDictionary *dictionary1,
+                                                ESpellDictionary *dictionary2);
+gint           e_spell_dictionary_compare      (ESpellDictionary *dictionary1,
+                                                ESpellDictionary *dictionary2);
+const gchar *  e_spell_dictionary_get_name     (ESpellDictionary *dictionary);
+const gchar *  e_spell_dictionary_get_code     (ESpellDictionary *dictionary);
+ESpellChecker *        e_spell_dictionary_ref_spell_checker
+                                               (ESpellDictionary *dictionary);
+gboolean       e_spell_dictionary_check_word   (ESpellDictionary *dictionary,
+                                                const gchar *word,
+                                                gsize length);
+void           e_spell_dictionary_learn_word   (ESpellDictionary *dictionary,
+                                                const gchar *word,
+                                                gsize length);
+void           e_spell_dictionary_ignore_word  (ESpellDictionary *dictionary,
+                                                const gchar *word,
+                                                gsize length);
+GList *                e_spell_dictionary_get_suggestions
+                                               (ESpellDictionary *dictionary,
+                                                const gchar *word,
+                                                gsize length);
+void           e_spell_dictionary_store_correction
+                                               (ESpellDictionary *dictionary,
+                                                const gchar *misspelled,
+                                                gsize misspelled_length,
+                                                const gchar *correction,
+                                                gsize correction_length);
+
+G_END_DECLS
+
+#endif /* E_SPELL_DICTIONARY_H */
diff --git a/e-util/e-spell-entry.c b/e-util/e-spell-entry.c
index 75c7a6a..4993612 100644
--- a/e-util/e-spell-entry.c
+++ b/e-util/e-spell-entry.c
@@ -23,8 +23,7 @@
 
 #include <libebackend/libebackend.h>
 
-#include <editor/gtkhtml-spell-language.h>
-#include <editor/gtkhtml-spell-checker.h>
+#include <e-util/e-spell-checker.h>
 
 #include "e-misc-utils.h"
 #include "e-spell-entry.h"
@@ -37,18 +36,20 @@ struct _ESpellEntryPrivate {
        PangoAttrList *attr_list;
        gint mark_character;
        gint entry_scroll_offset;
-       GSettings *settings;
        gboolean custom_checkers;
        gboolean checking_enabled;
-       GSList *checkers;
        gchar **words;
        gint *word_starts;
        gint *word_ends;
+
+       ESpellChecker *spell_checker;
+       guint active_languages_handler_id;
 };
 
 enum {
        PROP_0,
-       PROP_CHECKING_ENABLED
+       PROP_CHECKING_ENABLED,
+       PROP_SPELL_CHECKER
 };
 
 G_DEFINE_TYPE_WITH_CODE (
@@ -76,16 +77,12 @@ word_misspelled (ESpellEntry *entry,
        g_strlcpy (word, text + start, end - start + 1);
 
        if (g_unichar_isalpha (*word)) {
-               GSList *li;
+               ESpellChecker *spell_checker;
                gssize wlen = strlen (word);
 
-               for (li = entry->priv->checkers; li; li = g_slist_next (li)) {
-                       GtkhtmlSpellChecker *checker = li->data;
-                       if (gtkhtml_spell_checker_check_word (checker, word, wlen)) {
-                               result = FALSE;
-                               break;
-                       }
-               }
+               spell_checker = e_spell_entry_get_spell_checker (entry);
+               if (e_spell_checker_check_word (spell_checker, word, wlen))
+                       result = FALSE;
        }
 
        g_free (word);
@@ -160,8 +157,13 @@ spell_entry_recheck_all (ESpellEntry *entry)
        pango_attr_list_unref (entry->priv->attr_list);
        entry->priv->attr_list = pango_attr_list_new ();
 
-       if (e_spell_entry_get_checking_enabled (entry))
-               check_words = (entry->priv->checkers != NULL);
+       if (e_spell_entry_get_checking_enabled (entry)) {
+               ESpellChecker *spell_checker;
+
+               spell_checker = e_spell_entry_get_spell_checker (entry);
+               if (e_spell_checker_count_active_languages (spell_checker) > 0)
+                       check_words = TRUE;
+       }
 
        if (check_words) {
                /* Loop through words */
@@ -269,15 +271,15 @@ add_to_dictionary (GtkWidget *menuitem,
 {
        gchar *word;
        gint start, end;
-       GtkhtmlSpellChecker *checker;
+       ESpellDictionary *dict;
 
        get_word_extents_from_position (
                entry, &start, &end, entry->priv->mark_character);
        word = gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
 
-       checker = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
-       if (checker != NULL)
-               gtkhtml_spell_checker_add_word (checker, word, -1);
+       dict = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
+       if (dict != NULL)
+               e_spell_dictionary_learn_word (dict, word, -1);
 
        g_free (word);
 
@@ -300,18 +302,16 @@ static void
 ignore_all (GtkWidget *menuitem,
             ESpellEntry *entry)
 {
+       ESpellChecker *spell_checker;
        gchar *word;
        gint start, end;
-       GSList *li;
 
        get_word_extents_from_position (
                entry, &start, &end, entry->priv->mark_character);
        word = gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
 
-       for (li = entry->priv->checkers; li; li = g_slist_next (li)) {
-               GtkhtmlSpellChecker *checker = li->data;
-               gtkhtml_spell_checker_add_word_to_session (checker, word, -1);
-       }
+       spell_checker = e_spell_entry_get_spell_checker (entry);
+       e_spell_checker_ignore_word (spell_checker, word);
 
        g_free (word);
 
@@ -338,7 +338,7 @@ replace_word (GtkWidget *menuitem,
        const gchar *newword;
        gint start, end;
        gint cursor;
-       GtkhtmlSpellChecker *checker;
+       ESpellDictionary *dict;
 
        get_word_extents_from_position (
                entry, &start, &end, entry->priv->mark_character);
@@ -359,11 +359,11 @@ replace_word (GtkWidget *menuitem,
                GTK_EDITABLE (entry), newword, strlen (newword), &start);
        gtk_editable_set_position (GTK_EDITABLE (entry), cursor);
 
-       checker = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
+       dict = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
 
-       if (checker != NULL)
-               gtkhtml_spell_checker_store_replacement (
-                       checker, oldword, -1, newword, -1);
+       if (dict != NULL)
+               e_spell_dictionary_store_correction (
+                       dict, oldword, -1, newword, -1);
 
        g_free (oldword);
 }
@@ -371,13 +371,13 @@ replace_word (GtkWidget *menuitem,
 static void
 build_suggestion_menu (ESpellEntry *entry,
                        GtkWidget *menu,
-                       GtkhtmlSpellChecker *checker,
+                       ESpellDictionary *dict,
                        const gchar *word)
 {
        GtkWidget *mi;
        GList *suggestions, *iter;
 
-       suggestions = gtkhtml_spell_checker_get_suggestions (checker, word, -1);
+       suggestions = e_spell_dictionary_get_suggestions (dict, word, -1);
 
        if (suggestions == NULL) {
                /* no suggestions. Put something in the menu anyway... */
@@ -414,7 +414,7 @@ build_suggestion_menu (ESpellEntry *entry,
                        }
 
                        mi = gtk_menu_item_new_with_label (iter->data);
-                       g_object_set_data (G_OBJECT (mi), "spell-entry-checker", checker);
+                       g_object_set_data (G_OBJECT (mi), "spell-entry-checker", dict);
                        g_signal_connect (mi, "activate", G_CALLBACK (replace_word), entry);
                        gtk_widget_show (mi);
                        gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
@@ -428,35 +428,49 @@ static GtkWidget *
 build_spelling_menu (ESpellEntry *entry,
                      const gchar *word)
 {
-       GtkhtmlSpellChecker *checker;
+       ESpellChecker *spell_checker;
+       ESpellDictionary *dict;
        GtkWidget *topmenu, *mi;
+       GQueue queue = G_QUEUE_INIT;
+       gchar **active_languages;
+       guint ii, n_active_languages;
        gchar *label;
 
        topmenu = gtk_menu_new ();
 
-       if (entry->priv->checkers == NULL)
-               return topmenu;
+       spell_checker = e_spell_entry_get_spell_checker (entry);
+
+       active_languages = e_spell_checker_list_active_languages (
+               spell_checker, &n_active_languages);
+       for (ii = 0; ii < n_active_languages; ii++) {
+               dict = e_spell_checker_ref_dictionary (
+                       spell_checker, active_languages[ii]);
+               if (dict != NULL)
+                       g_queue_push_tail (&queue, dict);
+       }
+       g_strfreev (active_languages);
+
+       if (g_queue_is_empty (&queue))
+               goto exit;
 
        /* Suggestions */
-       if (entry->priv->checkers->next == NULL) {
-               checker = entry->priv->checkers->data;
-               build_suggestion_menu (entry, topmenu, checker, word);
+       if (n_active_languages == 1) {
+               dict = g_queue_peek_head (&queue);
+               build_suggestion_menu (entry, topmenu, dict, word);
        } else {
-               GSList *li;
                GtkWidget *menu;
-               const gchar *lang_name;
+               GList *list, *link;
 
-               for (li = entry->priv->checkers; li; li = g_slist_next (li)) {
-                       const GtkhtmlSpellLanguage *language;
+               list = g_queue_peek_head_link (&queue);
 
-                       checker = li->data;
-                       language = gtkhtml_spell_checker_get_language (checker);
-                       if (language == NULL)
-                               continue;
+               for (link = list; link != NULL; link = g_list_next (link)) {
+                       const gchar *lang_name;
 
-                       lang_name = gtkhtml_spell_language_get_name (language);
+                       dict = E_SPELL_DICTIONARY (link->data);
+
+                       lang_name = e_spell_dictionary_get_name (dict);
                        if (lang_name == NULL)
-                               lang_name = gtkhtml_spell_language_get_code (language);
+                               lang_name = e_spell_dictionary_get_code (dict);
                        if (lang_name == NULL)
                                lang_name = "???";
 
@@ -466,7 +480,7 @@ build_spelling_menu (ESpellEntry *entry,
                        gtk_menu_shell_append (GTK_MENU_SHELL (topmenu), mi);
                        menu = gtk_menu_new ();
                        gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu);
-                       build_suggestion_menu (entry, menu, checker, word);
+                       build_suggestion_menu (entry, menu, dict, word);
                }
        }
 
@@ -484,36 +498,34 @@ build_spelling_menu (ESpellEntry *entry,
                GTK_IMAGE_MENU_ITEM (mi),
                gtk_image_new_from_icon_name ("list-add", GTK_ICON_SIZE_MENU));
 
-       if (entry->priv->checkers->next == NULL) {
-               checker = entry->priv->checkers->data;
-               g_object_set_data (G_OBJECT (mi), "spell-entry-checker", checker);
+       if (n_active_languages == 1) {
+               dict = g_queue_peek_head (&queue);
+               g_object_set_data (G_OBJECT (mi), "spell-entry-checker", dict);
                g_signal_connect (
                        mi, "activate",
                        G_CALLBACK (add_to_dictionary), entry);
        } else {
-               GSList *li;
                GtkWidget *menu, *submi;
-               const gchar *lang_name;
+               GList *list, *link;
 
                menu = gtk_menu_new ();
                gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu);
 
-               for (li = entry->priv->checkers; li; li = g_slist_next (li)) {
-                       const GtkhtmlSpellLanguage *language;
+               list = g_queue_peek_head_link (&queue);
 
-                       checker = li->data;
-                       language = gtkhtml_spell_checker_get_language (checker);
-                       if (language == NULL)
-                               continue;
+               for (link = list; link != NULL; link = g_list_next (link)) {
+                       const gchar *lang_name;
+
+                       dict = E_SPELL_DICTIONARY (link->data);
 
-                       lang_name = gtkhtml_spell_language_get_name (language);
+                       lang_name = e_spell_dictionary_get_name (dict);
                        if (lang_name == NULL)
-                               lang_name = gtkhtml_spell_language_get_code (language);
+                               lang_name = e_spell_dictionary_get_code (dict);
                        if (lang_name == NULL)
                                lang_name = "???";
 
                        submi = gtk_menu_item_new_with_label (lang_name);
-                       g_object_set_data (G_OBJECT (submi), "spell-entry-checker", checker);
+                       g_object_set_data (G_OBJECT (submi), "spell-entry-checker", dict);
                        g_signal_connect (
                                submi, "activate",
                                G_CALLBACK (add_to_dictionary), entry);
@@ -535,6 +547,10 @@ build_spelling_menu (ESpellEntry *entry,
        gtk_widget_show_all (mi);
        gtk_menu_shell_append (GTK_MENU_SHELL (topmenu), mi);
 
+exit:
+       while (!g_queue_is_empty (&queue))
+               g_object_unref (g_queue_pop_head (&queue));
+
        return topmenu;
 }
 
@@ -580,10 +596,12 @@ spell_entry_populate_popup (ESpellEntry *entry,
                             GtkMenu *menu,
                             gpointer data)
 {
+       ESpellChecker *spell_checker;
        gint start, end;
        gchar *word;
 
-       if (entry->priv->checkers == NULL)
+       spell_checker = e_spell_entry_get_spell_checker (entry);
+       if (e_spell_checker_count_active_languages (spell_checker) == 0)
                return;
 
        get_word_extents_from_position (
@@ -606,8 +624,10 @@ static void
 spell_entry_changed (GtkEditable *editable)
 {
        ESpellEntry *entry = E_SPELL_ENTRY (editable);
+       ESpellChecker *spell_checker;
 
-       if (entry->priv->checkers == NULL)
+       spell_checker = e_spell_entry_get_spell_checker (entry);
+       if (e_spell_checker_count_active_languages (spell_checker) == 0)
                return;
 
        if (entry->priv->words != NULL) {
@@ -633,70 +653,6 @@ spell_entry_notify_scroll_offset (ESpellEntry *spell_entry)
                &spell_entry->priv->entry_scroll_offset, NULL);
 }
 
-static GList *
-spell_entry_load_spell_languages (void)
-{
-       GSettings *settings;
-       GList *spell_languages = NULL;
-       gchar **strv;
-       gint ii;
-
-       /* Ask GSettings for a list of spell check language codes. */
-       settings = g_settings_new ("org.gnome.evolution.mail");
-       strv = g_settings_get_strv (settings, "composer-spell-languages");
-       g_object_unref (settings);
-
-       /* Convert the codes to spell language structs. */
-       for (ii = 0; strv[ii] != NULL; ii++) {
-               gchar *language_code = strv[ii];
-               const GtkhtmlSpellLanguage *language;
-
-               language = gtkhtml_spell_language_lookup (language_code);
-               if (language != NULL)
-                       spell_languages = g_list_prepend (
-                               spell_languages, (gpointer) language);
-       }
-
-       g_strfreev (strv);
-
-       spell_languages = g_list_reverse (spell_languages);
-
-       /* Pick a default spell language if it came back empty. */
-       if (spell_languages == NULL) {
-               const GtkhtmlSpellLanguage *language;
-
-               language = gtkhtml_spell_language_lookup (NULL);
-
-               if (language) {
-                       spell_languages = g_list_prepend (
-                               spell_languages, (gpointer) language);
-               }
-       }
-
-       return spell_languages;
-}
-
-static void
-spell_entry_settings_changed (ESpellEntry *spell_entry,
-                              const gchar *key)
-{
-       GList *languages;
-
-       g_return_if_fail (spell_entry != NULL);
-
-       if (spell_entry->priv->custom_checkers)
-               return;
-
-       if (key && !g_str_equal (key, "composer-spell-languages"))
-               return;
-
-       languages = spell_entry_load_spell_languages ();
-       e_spell_entry_set_languages (spell_entry, languages);
-       g_list_free (languages);
-
-       spell_entry->priv->custom_checkers = FALSE;
-}
-
 static gint
 spell_entry_find_position (ESpellEntry *spell_entry,
                            gint x)
@@ -722,6 +678,15 @@ spell_entry_find_position (ESpellEntry *spell_entry,
 }
 
 static void
+spell_entry_active_languages_cb (ESpellChecker *spell_checker,
+                                 GParamSpec *pspec,
+                                 ESpellEntry *spell_entry)
+{
+       if (gtk_widget_get_realized (GTK_WIDGET (spell_entry)))
+               spell_entry_recheck_all (spell_entry);
+}
+
+static void
 spell_entry_set_property (GObject *object,
                           guint property_id,
                           const GValue *value,
@@ -733,6 +698,12 @@ spell_entry_set_property (GObject *object,
                                E_SPELL_ENTRY (object),
                                g_value_get_boolean (value));
                        return;
+
+               case PROP_SPELL_CHECKER:
+                       e_spell_entry_set_spell_checker (
+                               E_SPELL_ENTRY (object),
+                               g_value_get_object (value));
+                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -751,6 +722,13 @@ spell_entry_get_property (GObject *object,
                                e_spell_entry_get_checking_enabled (
                                E_SPELL_ENTRY (object)));
                        return;
+
+               case PROP_SPELL_CHECKER:
+                       g_value_set_object (
+                               value,
+                               e_spell_entry_get_spell_checker (
+                               E_SPELL_ENTRY (object)));
+                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -763,10 +741,14 @@ spell_entry_dispose (GObject *object)
 
        priv = E_SPELL_ENTRY_GET_PRIVATE (object);
 
-       g_slist_free_full (priv->checkers, (GDestroyNotify) g_object_unref);
-       priv->checkers = NULL;
+       if (priv->active_languages_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       priv->spell_checker,
+                       priv->active_languages_handler_id);
+               priv->active_languages_handler_id = 0;
+       }
 
-       g_clear_object (&priv->settings);
+       g_clear_object (&priv->spell_checker);
 
        if (priv->attr_list != NULL) {
                pango_attr_list_unref (priv->attr_list);
@@ -795,9 +777,22 @@ spell_entry_finalize (GObject *object)
 static void
 spell_entry_constructed (GObject *object)
 {
+       ESpellEntry *spell_entry;
+       ESpellChecker *spell_checker;
+
+       spell_entry = E_SPELL_ENTRY (object);
+
        /* Chain up to parent's constructed() method. */
        G_OBJECT_CLASS (e_spell_entry_parent_class)->constructed (object);
 
+       /* Install a default spell checker if there is not one already. */
+       spell_checker = e_spell_entry_get_spell_checker (spell_entry);
+       if (spell_checker == NULL) {
+               spell_checker = e_spell_checker_new ();
+               e_spell_entry_set_spell_checker (spell_entry, spell_checker);
+               g_object_unref (spell_checker);
+       }
+
        e_extensible_load_extensions (E_EXTENSIBLE (object));
 }
 
@@ -860,6 +855,17 @@ e_spell_entry_class_init (ESpellEntryClass *class)
                        TRUE,
                        G_PARAM_READWRITE |
                        G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_SPELL_CHECKER,
+               g_param_spec_object (
+                       "spell-checker",
+                       "Spell Checker",
+                       "The spell checker object",
+                       E_TYPE_SPELL_CHECKER,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -867,7 +873,6 @@ e_spell_entry_init (ESpellEntry *spell_entry)
 {
        spell_entry->priv = E_SPELL_ENTRY_GET_PRIVATE (spell_entry);
        spell_entry->priv->attr_list = pango_attr_list_new ();
-       spell_entry->priv->checkers = NULL;
        spell_entry->priv->checking_enabled = TRUE;
 
        g_signal_connect (
@@ -882,15 +887,6 @@ e_spell_entry_init (ESpellEntry *spell_entry)
        e_signal_connect_notify (
                spell_entry, "notify::scroll-offset",
                G_CALLBACK (spell_entry_notify_scroll_offset), NULL);
-
-       /* listen for languages changes */
-       spell_entry->priv->settings = g_settings_new ("org.gnome.evolution.mail");
-       g_signal_connect_swapped (
-               spell_entry->priv->settings, "changed",
-               G_CALLBACK (spell_entry_settings_changed), spell_entry);
-
-       /* load current settings */
-       spell_entry_settings_changed (spell_entry, NULL);
 }
 
 GtkWidget *
@@ -899,36 +895,6 @@ e_spell_entry_new (void)
        return g_object_new (E_TYPE_SPELL_ENTRY, NULL);
 }
 
-/* 'languages' consists of 'const GtkhtmlSpellLanguage *' */
-void
-e_spell_entry_set_languages (ESpellEntry *spell_entry,
-                             GList *languages)
-{
-       GList *iter;
-
-       g_return_if_fail (spell_entry != NULL);
-
-       spell_entry->priv->custom_checkers = TRUE;
-
-       if (spell_entry->priv->checkers)
-               g_slist_free_full (spell_entry->priv->checkers, g_object_unref);
-       spell_entry->priv->checkers = NULL;
-
-       for (iter = languages; iter; iter = g_list_next (iter)) {
-               const GtkhtmlSpellLanguage *language = iter->data;
-
-               if (language)
-                       spell_entry->priv->checkers = g_slist_prepend (
-                               spell_entry->priv->checkers,
-                               gtkhtml_spell_checker_new (language));
-       }
-
-       spell_entry->priv->checkers = g_slist_reverse (spell_entry->priv->checkers);
-
-       if (gtk_widget_get_realized (GTK_WIDGET (spell_entry)))
-               spell_entry_recheck_all (spell_entry);
-}
-
 gboolean
 e_spell_entry_get_checking_enabled (ESpellEntry *spell_entry)
 {
@@ -951,3 +917,66 @@ e_spell_entry_set_checking_enabled (ESpellEntry *spell_entry,
 
        g_object_notify (G_OBJECT (spell_entry), "checking-enabled");
 }
+
+/**
+ * e_spell_entry_get_spell_checker:
+ * @spell_entry: an #ESpellEntry
+ *
+ * Returns the #ESpellChecker being used for spell checking.  By default,
+ * #ESpellEntry creates its own #ESpellChecker, but this can be overridden
+ * through e_spell_entry_set_spell_checker().
+ *
+ * Returns: an #ESpellChecker
+ **/
+ESpellChecker *
+e_spell_entry_get_spell_checker (ESpellEntry *spell_entry)
+{
+       g_return_val_if_fail (E_IS_SPELL_ENTRY (spell_entry), NULL);
+
+       return spell_entry->priv->spell_checker;
+}
+
+/**
+ * e_spell_entry_set_spell_checker:
+ * @spell_entry: an #ESpellEntry
+ * @spell_checker: an #ESpellChecker
+ *
+ * Sets the #ESpellChecker to use for spell checking.  By default,
+ * #ESpellEntry creates its own #ESpellChecker.  This function can be
+ * useful for sharing an #ESpellChecker across multiple spell-checking
+ * widgets, so the active spell checking languages stay synchronized.
+ **/
+void
+e_spell_entry_set_spell_checker (ESpellEntry *spell_entry,
+                                 ESpellChecker *spell_checker)
+{
+       gulong handler_id;
+
+       g_return_if_fail (E_IS_SPELL_ENTRY (spell_entry));
+       g_return_if_fail (E_IS_SPELL_CHECKER (spell_checker));
+
+       if (spell_checker == spell_entry->priv->spell_checker)
+               return;
+
+       if (spell_entry->priv->spell_checker != NULL) {
+               g_signal_handler_disconnect (
+                       spell_entry->priv->spell_checker,
+                       spell_entry->priv->active_languages_handler_id);
+               g_object_unref (spell_entry->priv->spell_checker);
+       }
+
+       spell_entry->priv->spell_checker = g_object_ref (spell_checker);
+
+       handler_id = g_signal_connect (
+               spell_checker, "notify::active-languages",
+               G_CALLBACK (spell_entry_active_languages_cb),
+               spell_entry);
+
+       spell_entry->priv->active_languages_handler_id = handler_id;
+
+       g_object_notify (G_OBJECT (spell_entry), "spell-checker");
+
+       if (gtk_widget_get_realized (GTK_WIDGET (spell_entry)))
+               spell_entry_recheck_all (spell_entry);
+}
+
diff --git a/e-util/e-spell-entry.h b/e-util/e-spell-entry.h
index ed23cb0..a07d68f 100644
--- a/e-util/e-spell-entry.h
+++ b/e-util/e-spell-entry.h
@@ -24,6 +24,8 @@
 
 #include <gtk/gtk.h>
 
+#include <e-util/e-util.h>
+
 /* Standard GObject macros */
 #define E_TYPE_SPELL_ENTRY \
        (e_spell_entry_get_type ())
@@ -60,13 +62,14 @@ struct _ESpellEntryClass {
 
 GType          e_spell_entry_get_type          (void) G_GNUC_CONST;
 GtkWidget *    e_spell_entry_new               (void);
-void           e_spell_entry_set_languages     (ESpellEntry *spell_entry,
-                                                GList *languages);
 gboolean       e_spell_entry_get_checking_enabled
                                                (ESpellEntry *spell_entry);
 void           e_spell_entry_set_checking_enabled
                                                (ESpellEntry *spell_entry,
                                                 gboolean enable_checking);
+ESpellChecker *        e_spell_entry_get_spell_checker (ESpellEntry *spell_entry);
+void           e_spell_entry_set_spell_checker (ESpellEntry *spell_entry,
+                                                ESpellChecker *spell_checker);
 
 G_END_DECLS
 
diff --git a/e-util/e-util-enums.h b/e-util/e-util-enums.h
index 9913e4d..736a901 100644
--- a/e-util/e-util-enums.h
+++ b/e-util/e-util-enums.h
@@ -124,6 +124,227 @@ typedef enum {
        E_DURATION_DAYS
 } EDurationType;
 
+typedef enum {
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE = 0,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN,
+       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA
+} EHTMLEditorSelectionBlockFormat;
+
+/* The values match the actual size in <font size="n"> */
+typedef enum {
+       E_HTML_EDITOR_SELECTION_FONT_SIZE_TINY          = 1,
+       E_HTML_EDITOR_SELECTION_FONT_SIZE_SMALL         = 2,
+       E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL        = 3,
+       E_HTML_EDITOR_SELECTION_FONT_SIZE_BIG           = 4,
+       E_HTML_EDITOR_SELECTION_FONT_SIZE_BIGGER        = 5,
+       E_HTML_EDITOR_SELECTION_FONT_SIZE_LARGE         = 6,
+       E_HTML_EDITOR_SELECTION_FONT_SIZE_VERY_LARGE    = 7
+} EHTMLEditorSelectionFontSize;
+
+typedef enum {
+       E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
+       E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER,
+       E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT
+} EHTMLEditorSelectionAlignment;
+
+typedef enum {
+       E_HTML_EDITOR_SELECTION_GRANULARITY_CHARACTER,
+       E_HTML_EDITOR_SELECTION_GRANULARITY_WORD
+} EHTMLEditorSelectionGranularity;
+
+/**
+ * EHTMLEditorViewCommand:
+ * @E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR:
+ *   Sets background color to given value.
+ * @E_HTML_EDITOR_VIEW_COMMAND_BOLD:
+ *   Toggles bold formatting of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_COPY:
+ *   Copies current selection to clipboard.
+ * @E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK:
+ *   Converts current selection to a link that points to URL in value
+ * @E_HTML_EDITOR_VIEW_COMMAND_CUT:
+ *   Cuts current selection to clipboard.
+ * @E_HTML_EDITOR_VIEW_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR:
+ *   (XXX Explain me!)
+ * @E_HTML_EDITOR_VIEW_COMMAND_DELETE:
+ *   Deletes current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_FIND_STRING:
+ *   Highlights given string.
+ * @E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME:
+ *   Sets font name to given value.
+ * @E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE:
+ *   Sets font point size to given value (no units, just number)
+ * @E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE_DELTA:
+ *   Changes font size by given delta value (no units, just number)
+ * @E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR:
+ *   Sets font color to given value
+ * @E_HTML_EDITOR_VIEW_COMMAND_FORMAT_BLOCK:
+ *   Sets block type of current paragraph to given format. Allowed formats
+ *   are "BLOCKQUOTE", "H1", "H2", "H3", "H4", "H5", "H6", "P", "PRE" and
+ *   "ADDRESS".
+ * @E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE:
+ *   (XXX Explain me!)
+ * @E_HTML_EDITOR_VIEW_COMMAND_HILITE_COLOR:
+ *   Sets color in which results of "FindString" command should be
+ *   highlighted to given value.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INDENT:
+ *   Indents current paragraph by one level.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML:
+ *   Inserts give HTML code into document.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_HORIZONTAL_RULE:
+ *   Inserts a horizontal rule (&lt;HR&gt;) on current line.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_IMAGE:
+ *   Inserts an image with given source file.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_LINE_BREAK:
+ *   Breaks line at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT:
+ *   Breaks citation at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_ORDERED_LIST:
+ *   Creates an ordered list environment at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_PARAGRAPH:
+ *   Inserts a new paragraph at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT:
+ *   Inserts given text at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_UNORDERED_LIST:
+ *   Creates an undordered list environment at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_ITALIC:
+ *   Toggles italic formatting of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER:
+ *   Aligns current paragraph to center.
+ * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_FULL:
+ *   Justifies current paragraph to block.
+ * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_NONE:
+ *   Removes any justification or alignment of current paragraph.
+ * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT:
+ *   Aligns current paragraph to right.
+ * @E_HTML_EDITOR_VIEW_COMMAND_OUTDENT:
+ *   Outdents current paragraph by one level.
+ * @E_HTML_EDITOR_VIEW_COMMAND_PASTE:
+ *   Pastes clipboard content at current cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_PASTE_AND_MATCH_STYLE:
+ *   Pastes clipboard content and matches its style to style at current
+ *   cursor position.
+ * @E_HTML_EDITOR_VIEW_COMMAND_PASTE_AS_PLAIN_TEXT:
+ *   Pastes clipboard content at current cursor position removing any HTML
+ *   formatting.
+ * @E_HTML_EDITOR_VIEW_COMMAND_PRINT:
+ *   Print current document.
+ * @E_HTML_EDITOR_VIEW_COMMAND_REDO:
+ *   Redoes last action.
+ * @E_HTML_EDITOR_VIEW_COMMAND_REMOVE_FORMAT:
+ *   Removes any formatting of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_SELECT_ALL:
+ *   Extends selects to the entire document.
+ * @E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH:
+ *   Toggles strikethrough formatting.
+ * @E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS:
+ *   Toggles whether style should be defined in CSS "style" attribute of
+ *   elements or whether to use deprecated &lt;FONT&gt; tags. Depends on
+ *   whether given value is "true" or "false".
+ * @E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT:
+ *   Toggles subscript of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT:
+ *   Toggles superscript of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_TRANSPOSE:
+ *   (XXX Explain me!)
+ * @E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE:
+ *   Toggles underline formatting of current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_UNDO:
+ *   Undoes last action.
+ * @E_HTML_EDITOR_VIEW_COMMAND_UNLINK:
+ *   Removes active links (&lt;A&gt;) from current selection (if there's any).
+ * @E_HTML_EDITOR_VIEW_COMMAND_UNSELECT:
+ *   Cancels current selection.
+ * @E_HTML_EDITOR_VIEW_COMMAND_USE_CSS:
+ *   Whether to allow use of CSS or not depending on whether given value is
+ *   "true" or "false".
+ *
+ * Specifies the DOM command to execute in e_editor_widget_exec_command().
+ * Some commands require value to be passed in, which is always stated in the
+ * documentation.
+ */
+typedef enum {
+       E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR,
+       E_HTML_EDITOR_VIEW_COMMAND_BOLD,
+       E_HTML_EDITOR_VIEW_COMMAND_COPY,
+       E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK,
+       E_HTML_EDITOR_VIEW_COMMAND_CUT,
+       E_HTML_EDITOR_VIEW_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR,
+       E_HTML_EDITOR_VIEW_COMMAND_DELETE,
+       E_HTML_EDITOR_VIEW_COMMAND_FIND_STRING,
+       E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME,
+       E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE,
+       E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE_DELTA,
+       E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR,
+       E_HTML_EDITOR_VIEW_COMMAND_FORMAT_BLOCK,
+       E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE,
+       E_HTML_EDITOR_VIEW_COMMAND_HILITE_COLOR,
+       E_HTML_EDITOR_VIEW_COMMAND_INDENT,
+       E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML,
+       E_HTML_EDITOR_VIEW_COMMAND_INSERT_HORIZONTAL_RULE,
+       E_HTML_EDITOR_VIEW_COMMAND_INSERT_IMAGE,
+       E_HTML_EDITOR_VIEW_COMMAND_INSERT_LINE_BREAK,
+       E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT,
+       E_HTML_EDITOR_VIEW_COMMAND_INSERT_ORDERED_LIST,
+       E_HTML_EDITOR_VIEW_COMMAND_INSERT_PARAGRAPH,
+       E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT,
+       E_HTML_EDITOR_VIEW_COMMAND_INSERT_UNORDERED_LIST,
+       E_HTML_EDITOR_VIEW_COMMAND_ITALIC,
+       E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER,
+       E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_FULL,
+       E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_LEFT,
+       E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_NONE,
+       E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT,
+       E_HTML_EDITOR_VIEW_COMMAND_OUTDENT,
+       E_HTML_EDITOR_VIEW_COMMAND_PASTE,
+       E_HTML_EDITOR_VIEW_COMMAND_PASTE_AND_MATCH_STYLE,
+       E_HTML_EDITOR_VIEW_COMMAND_PASTE_AS_PLAIN_TEXT,
+       E_HTML_EDITOR_VIEW_COMMAND_PRINT,
+       E_HTML_EDITOR_VIEW_COMMAND_REDO,
+       E_HTML_EDITOR_VIEW_COMMAND_REMOVE_FORMAT,
+       E_HTML_EDITOR_VIEW_COMMAND_SELECT_ALL,
+       E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH,
+       E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS,
+       E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT,
+       E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT,
+       E_HTML_EDITOR_VIEW_COMMAND_TRANSPOSE,
+       E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE,
+       E_HTML_EDITOR_VIEW_COMMAND_UNDO,
+       E_HTML_EDITOR_VIEW_COMMAND_UNLINK,
+       E_HTML_EDITOR_VIEW_COMMAND_UNSELECT,
+       E_HTML_EDITOR_VIEW_COMMAND_USE_CSS
+} EHTMLEditorViewCommand;
+
+/**
+ * EImageLoadingPolicy:
+ * @E_IMAGE_LOADING_POLICY_NEVER:
+ *   Never load images from a remote server.
+ * @E_IMAGE_LOADING_POLICY_SOMETIMES:
+ *   Only load images from a remote server if the sender is a known contact.
+ * @E_IMAGE_LOADING_POLICY_ALWAYS:
+ *   Always load images from a remote server.
+ *
+ * Policy for loading remote image URLs in email.  Allowing images to be
+ * loaded from a remote server may have privacy implications.
+ **/
+typedef enum {
+       E_IMAGE_LOADING_POLICY_NEVER,
+       E_IMAGE_LOADING_POLICY_SOMETIMES,
+       E_IMAGE_LOADING_POLICY_ALWAYS
+} EImageLoadingPolicy;
+
 G_END_DECLS
 
 #endif /* E_UTIL_ENUMS_H */
diff --git a/e-util/e-util.h b/e-util/e-util.h
index 784858e..e9b06fe 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -81,6 +81,8 @@
 #include <e-util/e-client-cache.h>
 #include <e-util/e-client-combo-box.h>
 #include <e-util/e-client-selector.h>
+#include <e-util/e-color-chooser-widget.h>
+#include <e-util/e-color-combo.h>
 #include <e-util/e-config.h>
 #include <e-util/e-contact-store.h>
 #include <e-util/e-data-capture.h>
@@ -89,6 +91,11 @@
 #include <e-util/e-destination-store.h>
 #include <e-util/e-dialog-utils.h>
 #include <e-util/e-dialog-widgets.h>
+#include <e-util/e-emoticon-action.h>
+#include <e-util/e-emoticon-chooser-menu.h>
+#include <e-util/e-emoticon-chooser.h>
+#include <e-util/e-emoticon-tool-button.h>
+#include <e-util/e-emoticon.h>
 #include <e-util/e-event.h>
 #include <e-util/e-file-request.h>
 #include <e-util/e-file-utils.h>
@@ -103,9 +110,27 @@
 #include <e-util/e-filter-part.h>
 #include <e-util/e-filter-rule.h>
 #include <e-util/e-focus-tracker.h>
+#include <e-util/e-html-editor-actions.h>
+#include <e-util/e-html-editor-cell-dialog.h>
+#include <e-util/e-html-editor-dialog.h>
+#include <e-util/e-html-editor-find-dialog.h>
+#include <e-util/e-html-editor-hrule-dialog.h>
+#include <e-util/e-html-editor-image-dialog.h>
+#include <e-util/e-html-editor-link-dialog.h>
+#include <e-util/e-html-editor-page-dialog.h>
+#include <e-util/e-html-editor-paragraph-dialog.h>
+#include <e-util/e-html-editor-replace-dialog.h>
+#include <e-util/e-html-editor-selection.h>
+#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-html-editor-utils.h>
+#include <e-util/e-html-editor-view.h>
+#include <e-util/e-html-editor.h>
 #include <e-util/e-html-utils.h>
 #include <e-util/e-icon-factory.h>
 #include <e-util/e-image-chooser.h>
+#include <e-util/e-image-chooser-dialog.h>
 #include <e-util/e-import-assistant.h>
 #include <e-util/e-import.h>
 #include <e-util/e-interval-chooser.h>
@@ -220,7 +245,6 @@
 #include <e-util/e-url-entry.h>
 #include <e-util/e-util-enums.h>
 #include <e-util/e-util-enumtypes.h>
-#include <e-util/e-web-view-gtkhtml.h>
 #include <e-util/e-web-view-preview.h>
 #include <e-util/e-web-view.h>
 #include <e-util/e-widget-undo.h>
diff --git a/e-util/e-web-view.c b/e-util/e-web-view.c
index afb3cc9..0b83d81 100644
--- a/e-util/e-web-view.c
+++ b/e-util/e-web-view.c
@@ -359,8 +359,6 @@ static void
 web_view_init_web_settings (WebKitWebView *web_view)
 {
        WebKitWebSettings *web_settings;
-       GObjectClass *class;
-       GParamSpec *pspec;
 
        web_settings = webkit_web_settings_new ();
 
@@ -373,19 +371,9 @@ web_view_init_web_settings (WebKitWebView *web_view)
                "enable-offline-web-application-cache", FALSE,
                "enable-site-specific-quirks", TRUE,
                "enable-scripts", FALSE,
+               "respect-image-orientation", TRUE,
                NULL);
 
-       /* This property was introduced in WebKitGTK 2.0,
-        * so check for it and enable it if it's present. */
-       class = G_OBJECT_GET_CLASS (web_settings);
-       pspec = g_object_class_find_property (
-               class, "respect-image-orientation");
-       if (pspec != NULL) {
-               g_object_set (
-                       G_OBJECT (web_settings),
-                       pspec->name, TRUE, NULL);
-       }
-
        g_object_bind_property (
                web_settings, "enable-caret-browsing",
                web_view, "caret-mode",
@@ -3377,9 +3365,9 @@ e_web_view_install_request_handler (EWebView *web_view,
        soup_session_add_feature_by_type (session, handler_type);
 }
 
-static void
-create_and_add_css_style_sheet (WebKitDOMDocument *document,
-                                const gchar *style_sheet_id)
+void
+e_web_view_create_and_add_css_style_sheet (WebKitDOMDocument *document,
+                                           const gchar *style_sheet_id)
 {
        WebKitDOMElement *style_element;
 
@@ -3388,15 +3376,9 @@ create_and_add_css_style_sheet (WebKitDOMDocument *document,
        if (!style_element) {
                /* Create new <style> element */
                style_element = webkit_dom_document_create_element (document, "style", NULL);
-#if WEBKIT_CHECK_VERSION(2,2,0)  /* XXX should really be (2,1,something) */
                webkit_dom_element_set_id (
                        WEBKIT_DOM_ELEMENT (style_element),
                        style_sheet_id);
-#else
-               webkit_dom_html_element_set_id (
-                       WEBKIT_DOM_HTML_ELEMENT (style_element),
-                       style_sheet_id);
-#endif
                webkit_dom_html_style_element_set_media (
                        WEBKIT_DOM_HTML_STYLE_ELEMENT (style_element),
                        "screen");
@@ -3427,7 +3409,7 @@ add_css_rule_into_style_sheet (WebKitDOMDocument *document,
        style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id);
 
        if (!style_element) {
-               create_and_add_css_style_sheet (document, style_sheet_id);
+               e_web_view_create_and_add_css_style_sheet (document, style_sheet_id);
                style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id);
        }
 
@@ -3551,3 +3533,125 @@ e_web_view_add_css_rule_into_style_sheet (EWebView *view,
                selector,
                style);
 }
+
+gboolean
+element_has_id (WebKitDOMElement *element,
+                const gchar* id)
+{
+       gchar *element_id;
+
+       if (!element)
+               return FALSE;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       element_id = webkit_dom_element_get_id (element);
+
+       if (g_ascii_strcasecmp (element_id, id) != 0) {
+               g_free (element_id);
+               return FALSE;
+       }
+       g_free (element_id);
+
+       return TRUE;
+}
+
+gboolean
+element_has_tag (WebKitDOMElement *element,
+                 const gchar* tag)
+{
+       gchar *element_tag;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       element_tag = webkit_dom_node_get_local_name (WEBKIT_DOM_NODE (element));
+
+       if (g_ascii_strcasecmp (element_tag, tag) != 0) {
+               g_free (element_tag);
+               return FALSE;
+       }
+       g_free (element_tag);
+
+       return TRUE;
+}
+
+gboolean
+element_has_class (WebKitDOMElement *element,
+                const gchar* class)
+{
+       gchar *element_class;
+
+       if (!element)
+               return FALSE;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       element_class = webkit_dom_element_get_class_name (element);
+
+       if (g_strstr_len (element_class, -1, class)) {
+               g_free (element_class);
+               return TRUE;
+       }
+       g_free (element_class);
+
+       return FALSE;
+}
+
+void
+element_add_class (WebKitDOMElement *element,
+                   const gchar* class)
+{
+       gchar *element_class;
+       gchar *new_class;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element))
+               return;
+
+       if (element_has_class (element, class))
+               return;
+
+       element_class = webkit_dom_element_get_class_name (element);
+
+       if (g_strcmp0 (element_class, "") == 0)
+               new_class = g_strdup (class);
+       else
+               new_class = g_strconcat (element_class, " ", class, NULL);
+
+       webkit_dom_element_set_class_name (element, new_class);
+
+       g_free (element_class);
+       g_free (new_class);
+}
+
+void
+element_remove_class (WebKitDOMElement *element,
+                      const gchar* class)
+{
+       gchar *element_class;
+       GString *result;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element))
+               return;
+
+       if (!element_has_class (element, class))
+               return;
+
+       element_class = webkit_dom_element_get_class_name (element);
+
+       if (g_strcmp0 (element_class, class) == 0) {
+               webkit_dom_element_remove_attribute (element, "class");
+               g_free (element_class);
+               return;
+       }
+
+       result = e_str_replace_string (element_class, class, "");
+       if (result) {
+               webkit_dom_element_set_class_name (element, result->str);
+               g_string_free (result, TRUE);
+       }
+
+       g_free (element_class);
+}
diff --git a/e-util/e-web-view.h b/e-util/e-web-view.h
index 62bd045..cbfd2ce 100644
--- a/e-util/e-web-view.h
+++ b/e-util/e-web-view.h
@@ -197,11 +197,24 @@ GInputStream *    e_web_view_request_finish       (EWebView *web_view,
 void           e_web_view_install_request_handler
                                                (EWebView *web_view,
                                                 GType handler_type);
+void           e_web_view_create_and_add_css_style_sheet
+                                               (WebKitDOMDocument* document,
+                                                const gchar *style_sheet_id);
 void           e_web_view_add_css_rule_into_style_sheet
                                                (EWebView *web_view,
                                                 const gchar *style_sheet_id,
                                                 const gchar *selector,
                                                 const gchar *style);
+gboolean       element_has_id                  (WebKitDOMElement *element,
+                                                const gchar* id);
+gboolean       element_has_tag                 (WebKitDOMElement *element,
+                                                const gchar* tag);
+gboolean       element_has_class               (WebKitDOMElement *element,
+                                                const gchar* class);
+void           element_add_class               (WebKitDOMElement *element,
+                                                const gchar* class);
+void           element_remove_class            (WebKitDOMElement *element,
+                                                const gchar* class);
 G_END_DECLS
 
 #endif /* E_WEB_VIEW_H */
diff --git a/e-util/test-html-editor.c b/e-util/test-html-editor.c
new file mode 100644
index 0000000..488eb44
--- /dev/null
+++ b/e-util/test-html-editor.c
@@ -0,0 +1,497 @@
+/*
+ * e-html-editor-test.c
+ *
+ * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <gtk/gtk.h>
+#include <e-util/e-util.h>
+
+#include <glib/gi18n-lib.h>
+
+static const gchar *file_ui =
+"<ui>\n"
+"  <menubar name='main-menu'>\n"
+"    <menu action='file-menu'>\n"
+"     <menuitem action='save'/>\n"
+"     <menuitem action='save-as'/>\n"
+"     <separator/>\n"
+"     <menuitem action='print-preview'/>\n"
+"     <menuitem action='print'/>\n"
+"     <separator/>\n"
+"     <menuitem action='disable-editor'/>\n"
+"     <separator/>\n"
+"     <menuitem action='quit'/>\n"
+"    </menu>\n"
+"  </menubar>\n"
+"</ui>";
+
+static const gchar *view_ui =
+"<ui>\n"
+"  <menubar name='main-menu'>\n"
+"    <menu action='view-menu'>\n"
+"     <menuitem action='view-html-output'/>\n"
+"     <menuitem action='view-html-source'/>\n"
+"     <menuitem action='view-plain-source'/>\n"
+"     <menuitem action='view-inspector'/>\n"
+"    </menu>\n"
+"  </menubar>\n"
+"</ui>";
+
+static void
+handle_error (GError **error)
+{
+       if (*error != NULL) {
+               g_warning ("%s", (*error)->message);
+               g_clear_error (error);
+       }
+}
+
+static GtkPrintOperationResult
+print (EHTMLEditor *editor,
+       GtkPrintOperationAction action)
+{
+       WebKitWebFrame *frame;
+       GtkPrintOperation *operation;
+       GtkPrintOperationResult result;
+       GError *error = NULL;
+
+       operation = gtk_print_operation_new ();
+
+       frame = webkit_web_view_get_main_frame (
+               WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
+       result = webkit_web_frame_print_full (frame, operation, action, NULL);
+
+       g_object_unref (operation);
+       handle_error (&error);
+
+       return result;
+}
+
+static gint
+save_dialog (EHTMLEditor *editor)
+{
+       GtkWidget *dialog;
+       const gchar *filename;
+       gint response;
+
+       dialog = gtk_file_chooser_dialog_new (
+               _("Save As"),
+               GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))),
+               GTK_FILE_CHOOSER_ACTION_SAVE,
+               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+               GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+               NULL);
+
+       gtk_file_chooser_set_do_overwrite_confirmation (
+               GTK_FILE_CHOOSER (dialog), TRUE);
+
+       filename = e_html_editor_get_filename (editor);
+
+       if (filename != NULL)
+               gtk_file_chooser_set_filename (
+                       GTK_FILE_CHOOSER (dialog), filename);
+       else
+               gtk_file_chooser_set_current_name (
+                       GTK_FILE_CHOOSER (dialog), _("Untitled document"));
+
+       response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+       if (response == GTK_RESPONSE_ACCEPT) {
+               gchar *new_filename;
+
+               new_filename = gtk_file_chooser_get_filename (
+                       GTK_FILE_CHOOSER (dialog));
+               e_html_editor_set_filename (editor, new_filename);
+               g_free (new_filename);
+       }
+
+       gtk_widget_destroy (dialog);
+
+       return response;
+}
+
+static void
+view_source_dialog (EHTMLEditor *editor,
+                    const gchar *title,
+                    gboolean plain_text,
+                    gboolean show_source)
+{
+       GtkWidget *dialog;
+       GtkWidget *content;
+       GtkWidget *content_area;
+       GtkWidget *scrolled_window;
+       gchar * html;
+
+       dialog = gtk_dialog_new_with_buttons (
+               title,
+               GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))),
+               GTK_DIALOG_DESTROY_WITH_PARENT,
+               GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+               NULL);
+
+       content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+       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_scrolled_window_set_shadow_type (
+               GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
+       gtk_box_pack_start (
+               GTK_BOX (content_area),
+               scrolled_window, TRUE, TRUE, 0);
+
+       gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+       gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 6);
+       gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 300);
+
+       if (plain_text) {
+               html = e_html_editor_view_get_text_plain (
+                               e_html_editor_get_view (editor));
+       } else {
+               html = e_html_editor_view_get_text_html (
+                       e_html_editor_get_view (editor));
+       }
+
+       if (show_source || plain_text) {
+               GtkTextBuffer *buffer;
+
+               content = gtk_text_view_new ();
+               buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (content));
+               gtk_text_buffer_set_text (buffer, html, -1);
+               gtk_text_view_set_editable (GTK_TEXT_VIEW (content), FALSE);
+       } else {
+               content = webkit_web_view_new ();
+               webkit_web_view_load_string (
+                       WEBKIT_WEB_VIEW (content), html, NULL, NULL, NULL);
+       }
+       g_free (html);
+
+       gtk_container_add (GTK_CONTAINER (scrolled_window), content);
+       gtk_widget_show_all (scrolled_window);
+
+       gtk_dialog_run (GTK_DIALOG (dialog));
+       gtk_widget_destroy (dialog);
+}
+
+static void
+action_print_cb (GtkAction *action,
+                 EHTMLEditor *editor)
+{
+       print (editor, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
+}
+
+static void
+action_print_preview_cb (GtkAction *action,
+                         EHTMLEditor *editor)
+{
+       print (editor, GTK_PRINT_OPERATION_ACTION_PREVIEW);
+}
+
+static void
+action_quit_cb (GtkAction *action,
+                EHTMLEditor *editor)
+{
+       gtk_main_quit ();
+}
+
+static void
+action_save_cb (GtkAction *action,
+                EHTMLEditor *editor)
+{
+       const gchar *filename;
+       gboolean as_html;
+       GError *error = NULL;
+
+       if (e_html_editor_get_filename (editor) == NULL)
+               if (save_dialog (editor) == GTK_RESPONSE_CANCEL)
+                       return;
+
+       filename = e_html_editor_get_filename (editor);
+       as_html = (e_html_editor_view_get_html_mode (e_html_editor_get_view (editor)));
+
+       e_html_editor_save (editor, filename, as_html, &error);
+       handle_error (&error);
+}
+
+static void
+action_save_as_cb (GtkAction *action,
+                   EHTMLEditor *editor)
+{
+       const gchar *filename;
+       gboolean as_html;
+       GError *error = NULL;
+
+       if (save_dialog (editor) == GTK_RESPONSE_CANCEL)
+               return;
+
+       filename = e_html_editor_get_filename (editor);
+       as_html = (e_html_editor_view_get_html_mode (e_html_editor_get_view (editor)));
+
+       e_html_editor_save (editor, filename, as_html, &error);
+       handle_error (&error);
+}
+
+static void
+action_toggle_editor (GtkAction *action,
+                      EHTMLEditor *editor)
+{
+       EHTMLEditorView *view;
+
+       view = e_html_editor_get_view (editor);
+       webkit_web_view_set_editable (
+               WEBKIT_WEB_VIEW (view),
+               ! webkit_web_view_get_editable (WEBKIT_WEB_VIEW (view)));
+}
+
+static void
+action_view_html_output (GtkAction *action,
+                         EHTMLEditor *editor)
+{
+       view_source_dialog (editor, _("HTML Output"), FALSE, FALSE);
+}
+
+static void
+action_view_html_source (GtkAction *action,
+                         EHTMLEditor *editor)
+{
+       view_source_dialog (editor, _("HTML Source"), FALSE, TRUE);
+}
+
+static void
+action_view_plain_source (GtkAction *action,
+                          EHTMLEditor *editor)
+{
+       view_source_dialog (editor, _("Plain Source"), TRUE, FALSE);
+}
+
+static void
+action_view_inspector (GtkAction *action,
+                       EHTMLEditor *editor)
+{
+       WebKitWebInspector *inspector;
+       EHTMLEditorView *view;
+
+       view = e_html_editor_get_view (editor);
+       inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (view));
+
+       webkit_web_inspector_show (inspector);
+}
+
+static GtkActionEntry file_entries[] = {
+
+       { "print",
+         GTK_STOCK_PRINT,
+         N_("_Print..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_print_cb) },
+
+       { "print-preview",
+         GTK_STOCK_PRINT_PREVIEW,
+         N_("Print Pre_view"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_print_preview_cb) },
+
+       { "quit",
+         GTK_STOCK_QUIT,
+         N_("_Quit"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_quit_cb) },
+
+       { "save",
+         GTK_STOCK_SAVE,
+         N_("_Save"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_save_cb) },
+
+       { "save-as",
+         GTK_STOCK_SAVE_AS,
+         N_("Save _As..."),
+         NULL,
+         NULL,
+         G_CALLBACK (action_save_as_cb) },
+
+       { "disable-editor",
+         NULL,
+         N_("Disable/Enable Editor"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_toggle_editor) },
+
+       { "file-menu",
+         NULL,
+         N_("_File"),
+         NULL,
+         NULL,
+         NULL }
+};
+
+static GtkActionEntry view_entries[] = {
+
+       { "view-html-output",
+         NULL,
+         N_("HTML _Output"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_view_html_output) },
+
+       { "view-html-source",
+         NULL,
+         N_("_HTML Source"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_view_html_source) },
+
+       { "view-plain-source",
+         NULL,
+         N_("_Plain Source"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_view_plain_source) },
+
+       { "view-inspector",
+         NULL,
+         N_("Inspector"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_view_inspector) },
+
+       { "view-menu",
+         NULL,
+         N_("_View"),
+         NULL,
+         NULL,
+         NULL }
+};
+
+static WebKitWebView *
+open_inspector (WebKitWebInspector *inspector,
+                WebKitWebView *webview,
+                gpointer user_data)
+{
+       GtkWidget *window;
+       GtkWidget *inspector_view;
+
+       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+       inspector_view = webkit_web_view_new ();
+
+       gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (inspector_view));
+
+       gtk_widget_set_size_request (window, 600, 480);
+       gtk_widget_show (window);
+
+       return WEBKIT_WEB_VIEW (inspector_view);
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       GtkActionGroup *action_group;
+       GtkUIManager *manager;
+       GtkWidget *container;
+       GtkWidget *widget;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       WebKitWebInspector *inspector;
+
+       GError *error = NULL;
+
+       bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR);
+       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+       textdomain (GETTEXT_PACKAGE);
+
+       gtk_init (&argc, &argv);
+
+       editor = g_object_ref_sink (e_html_editor_new ());
+       view = e_html_editor_get_view (editor);
+
+       inspector = webkit_web_view_get_inspector (
+               WEBKIT_WEB_VIEW (view));
+       g_signal_connect (
+               inspector, "inspect-web-view",
+               G_CALLBACK (open_inspector), NULL);
+
+       widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+       gtk_widget_set_size_request (widget, 600, 400);
+       gtk_widget_show (widget);
+
+       g_signal_connect_swapped (
+               widget, "destroy",
+               G_CALLBACK (gtk_main_quit), NULL);
+
+       container = widget;
+
+       widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       gtk_widget_show (widget);
+
+       container = widget;
+
+       widget = e_html_editor_get_managed_widget (editor, "/main-menu");
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       widget = GTK_WIDGET (editor);
+       gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+       gtk_widget_show (widget);
+
+       manager = e_html_editor_get_ui_manager (editor);
+
+       gtk_ui_manager_add_ui_from_string (manager, file_ui, -1, &error);
+       handle_error (&error);
+
+       gtk_ui_manager_add_ui_from_string (manager, view_ui, -1, &error);
+       handle_error (&error);
+
+       action_group = gtk_action_group_new ("file");
+       gtk_action_group_set_translation_domain (
+               action_group, GETTEXT_PACKAGE);
+       gtk_action_group_add_actions (
+               action_group, file_entries,
+               G_N_ELEMENTS (file_entries), editor);
+       gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+       action_group = gtk_action_group_new ("view");
+       gtk_action_group_set_translation_domain (
+               action_group, GETTEXT_PACKAGE);
+       gtk_action_group_add_actions (
+               action_group, view_entries,
+               G_N_ELEMENTS (view_entries), editor);
+       gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+       gtk_ui_manager_ensure_update (manager);
+
+       g_signal_connect (
+               editor, "destroy",
+               G_CALLBACK (gtk_main_quit), NULL);
+
+       gtk_main ();
+
+       g_object_unref (editor);
+
+       return 0;
+}
diff --git a/em-format/Makefile.am b/em-format/Makefile.am
index dabc083..7827d79 100644
--- a/em-format/Makefile.am
+++ b/em-format/Makefile.am
@@ -52,9 +52,9 @@ libevolution_mail_formatter_la_CPPFLAGS =             \
        -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\"    \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
        $(LIBSOUP_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 if ENABLE_SMIME
 SMIME_EXTENSIONS = e-mail-parser-application-smime.c
@@ -139,9 +139,9 @@ libevolution_mail_formatter_la_LIBADD =                     \
        $(top_builddir)/libemail-engine/libemail-engine.la \
        $(EVOLUTION_DATA_SERVER_LIBS)                   \
        $(GNOME_PLATFORM_LIBS)                          \
-       $(GTKHTML_LIBS)                                 \
        $(LIBSOUP_LIBS)                                 \
-       $(SMIME_LIBS)
+       $(SMIME_LIBS)                                   \
+       $(NULL)
 
 BUILT_SOURCES = \
        $(ENUM_GENERATED) \
diff --git a/em-format/e-mail-formatter-enums.h b/em-format/e-mail-formatter-enums.h
index 862d66e..e7b1868 100644
--- a/em-format/e-mail-formatter-enums.h
+++ b/em-format/e-mail-formatter-enums.h
@@ -61,24 +61,6 @@ typedef enum { /*< flags >*/
 } EMailFormatterQuoteFlags;
 
 /**
- * EMailImageLoadingPolicy:
- * @E_MAIL_IMAGE_LOADING_POLICY_NEVER:
- *   Never load images from a remote server.
- * @E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES:
- *   Only load images from a remote server if the sender is a known contact.
- * @E_MAIL_IMAGE_LOADING_POLICY_ALWAYS:
- *   Always load images from a remote server.
- *
- * Policy for loading remote image URLs in email.  Allowing images to be
- * loaded from a remote server may have privacy implications.
- **/
-typedef enum {
-       E_MAIL_IMAGE_LOADING_POLICY_NEVER,
-       E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES,
-       E_MAIL_IMAGE_LOADING_POLICY_ALWAYS
-} EMailImageLoadingPolicy;
-
-/**
  * EMailParserExtensionFlags:
  * @E_MAIL_PARSER_EXTENSION_INLINE:
  *    Don't parse as attachment.
diff --git a/em-format/e-mail-formatter-quote-attachment.c b/em-format/e-mail-formatter-quote-attachment.c
index 0ec4889..dcc5300 100644
--- a/em-format/e-mail-formatter-quote-attachment.c
+++ b/em-format/e-mail-formatter-quote-attachment.c
@@ -96,10 +96,7 @@ emfqe_attachment_format (EMailFormatterExtension *extension,
        g_free (html);
        g_free (text);
 
-       string =
-               "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
-               "key=\"orig\" value=\"1\">-->\n"
-               "<blockquote type=cite>\n";
+       string = "<blockquote type=cite>\n";
        g_output_stream_write_all (
                stream, string, strlen (string), NULL, cancellable, NULL);
 
@@ -107,9 +104,7 @@ emfqe_attachment_format (EMailFormatterExtension *extension,
                formatter, context, attachment_view_part,
                stream, NULL, cancellable);
 
-       string =
-               "</blockquote><!--+GtkHTML:"
-               "<DATA class=\"ClueFlow\" clear=\"orig\">-->";
+       string = "</blockquote>";
        g_output_stream_write_all (
                stream, string, strlen (string), NULL, cancellable, NULL);
 
diff --git a/em-format/e-mail-formatter-quote-text-html.c b/em-format/e-mail-formatter-quote-text-html.c
index f6a47a9..8c6433c 100644
--- a/em-format/e-mail-formatter-quote-text-html.c
+++ b/em-format/e-mail-formatter-quote-text-html.c
@@ -59,7 +59,7 @@ emqfe_text_html_format (EMailFormatterExtension *extension,
 
        qf_context = (EMailFormatterQuoteContext *) context;
 
-       string = "\n<!-- text/html -->\n";
+       string = "<!-- text/html -->";
        g_output_stream_write_all (
                stream, string, strlen (string), NULL, cancellable, NULL);
 
diff --git a/em-format/e-mail-formatter-quote-text-plain.c b/em-format/e-mail-formatter-quote-text-plain.c
index 318762c..ce97e86 100644
--- a/em-format/e-mail-formatter-quote-text-plain.c
+++ b/em-format/e-mail-formatter-quote-text-plain.c
@@ -73,8 +73,10 @@ emqfe_text_plain_format (EMailFormatterExtension *extension,
                CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
                CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
 
+       /* XXX Should we define a separate EMailFormatter property
+        *     for using CAMEL_MIME_FILTER_TOHTML_QUOTE_CITATION? */
        if (e_mail_formatter_get_mark_citations (formatter))
-               text_flags |= CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
+               text_flags |= CAMEL_MIME_FILTER_TOHTML_QUOTE_CITATION;
 
        /* Check for RFC 2646 flowed text. */
        type = camel_mime_part_get_content_type (mime_part);
diff --git a/em-format/e-mail-formatter-quote.c b/em-format/e-mail-formatter-quote.c
index 8eda05f..b402eda 100644
--- a/em-format/e-mail-formatter-quote.c
+++ b/em-format/e-mail-formatter-quote.c
@@ -57,7 +57,6 @@ mail_formatter_quote_run (EMailFormatter *formatter,
 {
        EMailFormatterQuote *qf;
        EMailFormatterQuoteContext *qf_context;
-       GSettings *settings;
        GQueue queue = G_QUEUE_INIT;
        GList *head, *link;
        const gchar *string;
@@ -74,31 +73,6 @@ mail_formatter_quote_run (EMailFormatter *formatter,
                G_SEEKABLE (stream),
                0, G_SEEK_SET, NULL, NULL);
 
-       settings = g_settings_new ("org.gnome.evolution.mail");
-       if (g_settings_get_boolean (settings, "composer-top-signature")) {
-               string = "<br>\n";
-               g_output_stream_write_all (
-                       stream, string, strlen (string),
-                       NULL, cancellable, NULL);
-       }
-       g_object_unref (settings);
-
-       if (qf->priv->credits != NULL && *qf->priv->credits != '\0') {
-               g_output_stream_write_all (
-                       stream, qf->priv->credits,
-                       strlen (qf->priv->credits),
-                       NULL, cancellable, NULL);
-       }
-
-       if (qf->priv->flags & E_MAIL_FORMATTER_QUOTE_FLAG_CITE) {
-               string = "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
-                       "key=\"orig\" value=\"1\">-->\n"
-                       "<blockquote type=cite>\n";
-               g_output_stream_write_all (
-                       stream, string, strlen (string),
-                       NULL, cancellable, NULL);
-       }
-
        e_mail_part_list_queue_parts (context->part_list, NULL, &queue);
 
        head = g_queue_peek_head_link (&queue);
@@ -133,9 +107,33 @@ mail_formatter_quote_run (EMailFormatter *formatter,
        while (!g_queue_is_empty (&queue))
                g_object_unref (g_queue_pop_head (&queue));
 
+       /* Before we were inserting the BR elements and the credits in front of
+        * the actual HTML code of the message. But this was wrong as when WebKit
+        * was loading the given HTML code that looked like
+        * <br>CREDITS<html>MESSAGE_CODE</html> WebKit parsed it like
+        * <html><br>CREDITS</html><html>MESSAGE_CODE</html>. As no elements are
+        * allowed outside of the HTML root element WebKit wrapped them into
+        * another HTML root element. Afterwards the first root element was
+        * treated as the primary one and all the elements from the second's root
+        * HEAD and BODY elements were moved to the first one.
+        * Thus the HTML that was loaded into composer contained the i.e. META
+        * or STYLE definitions in the body.
+        * So if we want to put something into the message we have to put it into
+        * the special span element and it will be moved to body in EHTMLEditorView */
+       if (qf->priv->credits && *qf->priv->credits) {
+               gchar *credits = g_strdup_printf (
+                       "<span class=\"-x-evo-to-body\"><pre>%s</pre></span>", qf->priv->credits);
+               g_output_stream_write_all (
+                       stream, credits, strlen (credits),
+                       NULL, cancellable, NULL);
+               g_free (credits);
+       }
+
+       /* If we want to cite the message we have to append the special span element
+        * after the message and cite it in EHTMLEditorView because of reasons
+        * mentioned above */
        if (qf->priv->flags & E_MAIL_FORMATTER_QUOTE_FLAG_CITE) {
-               string = "</blockquote><!--+GtkHTML:"
-                       "<DATA class=\"ClueFlow\" clear=\"orig\">-->";
+               string = "<span class=\"-x-evo-cite-body\"><span>";
                g_output_stream_write_all (
                        stream, string, strlen (string),
                        NULL, cancellable, NULL);
diff --git a/em-format/e-mail-formatter.c b/em-format/e-mail-formatter.c
index 2efe8eb..a21f51c 100644
--- a/em-format/e-mail-formatter.c
+++ b/em-format/e-mail-formatter.c
@@ -41,7 +41,7 @@
 typedef struct _AsyncContext AsyncContext;
 
 struct _EMailFormatterPrivate {
-       EMailImageLoadingPolicy image_loading_policy;
+       EImageLoadingPolicy image_loading_policy;
 
        gboolean show_sender_photo;
        gboolean show_real_date;
@@ -705,8 +705,8 @@ e_mail_formatter_class_init (EMailFormatterClass *class)
                        "image-loading-policy",
                        "Image Loading Policy",
                        NULL,
-                       E_TYPE_MAIL_IMAGE_LOADING_POLICY,
-                       E_MAIL_IMAGE_LOADING_POLICY_NEVER,
+                       E_TYPE_IMAGE_LOADING_POLICY,
+                       E_IMAGE_LOADING_POLICY_NEVER,
                        G_PARAM_READWRITE |
                        G_PARAM_STATIC_STRINGS));
 
@@ -1229,7 +1229,7 @@ e_mail_formatter_update_style (EMailFormatter *formatter,
        class->update_style (formatter, state);
 }
 
-EMailImageLoadingPolicy
+EImageLoadingPolicy
 e_mail_formatter_get_image_loading_policy (EMailFormatter *formatter)
 {
        g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), 0);
@@ -1239,7 +1239,7 @@ e_mail_formatter_get_image_loading_policy (EMailFormatter *formatter)
 
 void
 e_mail_formatter_set_image_loading_policy (EMailFormatter *formatter,
-                                           EMailImageLoadingPolicy policy)
+                                           EImageLoadingPolicy policy)
 {
        g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
 
diff --git a/em-format/e-mail-formatter.h b/em-format/e-mail-formatter.h
index bbd61f2..a05755c 100644
--- a/em-format/e-mail-formatter.h
+++ b/em-format/e-mail-formatter.h
@@ -143,12 +143,12 @@ void              e_mail_formatter_set_color      (EMailFormatter *formatter,
 void           e_mail_formatter_update_style   (EMailFormatter *formatter,
                                                 GtkStateFlags state);
 
-EMailImageLoadingPolicy
+EImageLoadingPolicy
                e_mail_formatter_get_image_loading_policy
                                                (EMailFormatter *formatter);
 void           e_mail_formatter_set_image_loading_policy
                                                (EMailFormatter *formatter,
-                                                EMailImageLoadingPolicy policy);
+                                                EImageLoadingPolicy policy);
 
 gboolean       e_mail_formatter_get_mark_citations
                                                (EMailFormatter *formatter);
diff --git a/libemail-engine/Makefile.am b/libemail-engine/Makefile.am
index 3064d82..4dd1bd4 100644
--- a/libemail-engine/Makefile.am
+++ b/libemail-engine/Makefile.am
@@ -24,7 +24,6 @@ libemail_engine_la_CPPFLAGS = \
        -DLIBEMAIL_ENGINE_COMPILATION \
        $(EVOLUTION_DATA_SERVER_CFLAGS) \
        $(GNOME_PLATFORM_CFLAGS) \
-       $(GTKHTML_CFLAGS) \
        -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
        $(CODE_COVERAGE_CFLAGS) \
        $(NULL)
@@ -81,7 +80,6 @@ libemail_engine_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la \
        $(EVOLUTION_DATA_SERVER_LIBS) \
        $(GNOME_PLATFORM_LIBS) \
-       $(GTKHTML_LIBS) \
        $(NULL)
 
 libemail_engine_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/mail/Makefile.am b/mail/Makefile.am
index d40922e..d0b7220 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -46,10 +46,10 @@ libevolution_mail_la_CPPFLAGS =                             \
        $(GNOME_PLATFORM_CFLAGS)                        \
        $(CERT_UI_CFLAGS)                               \
        $(CANBERRA_CFLAGS)                              \
-       $(GTKHTML_CFLAGS)                               \
        $(LIBCRYPTUI_CFLAGS)                            \
        $(LIBSOUP_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 mailinclude_HEADERS =                                  \
        e-http-request.h                                \
@@ -225,7 +225,6 @@ libevolution_mail_la_LIBADD =                               \
        $(GNOME_PLATFORM_LIBS)                          \
        $(CERT_UI_LIBS)                                 \
        $(CANBERRA_LIBS)                                \
-       $(GTKHTML_LIBS)                                 \
        $(SMIME_LIBS)                                   \
        $(LIBCRYPTUI_LIBS)                              \
        $(LIBSOUP_LIBS)                                 \
diff --git a/mail/e-http-request.c b/mail/e-http-request.c
index 534e30b..39db7a1 100644
--- a/mail/e-http-request.c
+++ b/mail/e-http-request.c
@@ -165,17 +165,16 @@ handle_http_request (GSimpleAsyncResult *res,
        SoupRequest *soup_request;
        SoupSession *soup_session;
        gchar *evo_uri, *uri;
-       gchar *mail_uri;
+       gchar *mail_uri = NULL;
        GInputStream *stream;
        gboolean force_load_images = FALSE;
-       EMailImageLoadingPolicy image_policy;
+       EImageLoadingPolicy image_policy;
        gchar *uri_md5;
        EShell *shell;
        GSettings *settings;
-       const gchar *user_cache_dir;
+       const gchar *user_cache_dir, *soup_query;
        CamelDataCache *cache;
        GIOStream *cache_stream;
-       GHashTable *query;
        gint uri_len;
 
        if (g_cancellable_is_cancelled (cancellable))
@@ -185,24 +184,27 @@ handle_http_request (GSimpleAsyncResult *res,
 
        soup_request = SOUP_REQUEST (source_object);
        soup_session = soup_request_get_session (soup_request);
+       soup_uri = soup_request_get_uri (soup_request);
 
        /* Remove the __evo-mail query */
-       soup_uri = soup_request_get_uri (soup_request);
-       g_return_if_fail (soup_uri_get_query (soup_uri) != NULL);
+       soup_query = soup_uri_get_query (soup_uri);
+       if (soup_query) {
+               GHashTable *query;
 
-       query = soup_form_decode (soup_uri_get_query (soup_uri));
-       mail_uri = g_hash_table_lookup (query, "__evo-mail");
-       if (mail_uri)
-               mail_uri = g_strdup (mail_uri);
+               query = soup_form_decode (soup_uri_get_query (soup_uri));
+               mail_uri = g_hash_table_lookup (query, "__evo-mail");
+               if (mail_uri)
+                       mail_uri = g_strdup (mail_uri);
 
-       g_hash_table_remove (query, "__evo-mail");
+               g_hash_table_remove (query, "__evo-mail");
 
-       /* Remove __evo-load-images if present (and in such case set
-        * force_load_images to TRUE) */
-       force_load_images = g_hash_table_remove (query, "__evo-load-images");
+               /* Remove __evo-load-images if present (and in such case set
+                * force_load_images to TRUE) */
+               force_load_images = g_hash_table_remove (query, "__evo-load-images");
 
-       soup_uri_set_query_from_form (soup_uri, query);
-       g_hash_table_unref (query);
+               soup_uri_set_query_from_form (soup_uri, query);
+               g_hash_table_unref (query);
+       }
 
        evo_uri = soup_uri_to_string (soup_uri, FALSE);
 
@@ -305,7 +307,7 @@ handle_http_request (GSimpleAsyncResult *res,
        /* Item not found in cache, but image loading policy allows us to fetch
         * it from the interwebs */
        if (!force_load_images && mail_uri != NULL &&
-           (image_policy == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES)) {
+           (image_policy == E_IMAGE_LOADING_POLICY_SOMETIMES)) {
                CamelObjectBag *registry;
                gchar *decoded_uri;
                EMailPartList *part_list;
@@ -350,7 +352,7 @@ handle_http_request (GSimpleAsyncResult *res,
                g_free (decoded_uri);
        }
 
-       if ((image_policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS) ||
+       if ((image_policy == E_IMAGE_LOADING_POLICY_ALWAYS) ||
            force_load_images) {
 
                SoupSession *temp_session;
@@ -488,27 +490,25 @@ http_request_send_async (SoupRequest *request,
                          gpointer user_data)
 {
        GSimpleAsyncResult *simple;
-       SoupURI *uri;
-       const gchar *enc;
-       GHashTable *query;
-
-       uri = soup_request_get_uri (request);
-       g_return_if_fail (soup_uri_get_query (uri) != NULL);
-
-       query = soup_form_decode (soup_uri_get_query (uri));
 
        d ({
-               gchar *uri_str = soup_uri_to_string (uri, FALSE);
-               printf ("received request for %s\n", uri_str);
-               g_free (uri_str);
-       });
+               const gchar *soup_query;
+               SoupURI *uri;
 
-       enc = g_hash_table_lookup (query, "__evo-mail");
+               uri = soup_request_get_uri (request);
+               soup_query = soup_uri_get_query (uri);
 
-       if (enc == NULL || *enc == '\0') {
-               g_hash_table_destroy (query);
-               return;
-       }
+               if (soup_query) {
+                       gchar *uri_str;
+                       GHashTable *query;
+
+                       query = soup_form_decode (soup_uri_get_query (uri));
+                       uri_str = soup_uri_to_string (uri, FALSE);
+                       printf ("received request for %s\n", uri_str);
+                       g_free (uri_str);
+                       g_hash_table_destroy (query);
+               }
+       });
 
        simple = g_simple_async_result_new (
                G_OBJECT (request), callback,
@@ -521,8 +521,6 @@ http_request_send_async (SoupRequest *request,
                G_PRIORITY_DEFAULT, cancellable);
 
        g_object_unref (simple);
-
-       g_hash_table_destroy (query);
 }
 
 static GInputStream *
diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c
index 61751df..7741a40 100644
--- a/mail/e-mail-display.c
+++ b/mail/e-mail-display.c
@@ -142,11 +142,11 @@ formatter_image_loading_policy_changed_cb (GObject *object,
 {
        EMailDisplay *display = user_data;
        EMailFormatter *formatter = E_MAIL_FORMATTER (object);
-       EMailImageLoadingPolicy policy;
+       EImageLoadingPolicy policy;
 
        policy = e_mail_formatter_get_image_loading_policy (formatter);
 
-       if (policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS)
+       if (policy == E_IMAGE_LOADING_POLICY_ALWAYS)
                e_mail_display_load_images (display);
        else
                e_mail_display_reload (display);
@@ -1294,7 +1294,7 @@ mail_display_redirect_uri (EWebView *web_view,
                SoupURI *soup_uri;
                GHashTable *query;
                gboolean image_exists;
-               EMailImageLoadingPolicy image_policy;
+               EImageLoadingPolicy image_policy;
 
                /* Check Evolution's cache */
                image_exists = mail_display_image_exists_in_cache (uri);
@@ -1305,7 +1305,7 @@ mail_display_redirect_uri (EWebView *web_view,
                image_policy = e_mail_formatter_get_image_loading_policy (
                        display->priv->formatter);
                if (!image_exists && !display->priv->force_image_load &&
-                   (image_policy == E_MAIL_IMAGE_LOADING_POLICY_NEVER)) {
+                   (image_policy == E_IMAGE_LOADING_POLICY_NEVER)) {
                        return g_strdup ("about:blank");
                }
 
@@ -1352,39 +1352,46 @@ chainup:
                redirect_uri (web_view, uri);
 }
 
-static gchar *
-mail_display_suggest_filename (EWebView *web_view,
-                               const gchar *uri)
+static CamelMimePart *
+camel_mime_part_from_cid (EMailDisplay *display,
+                          const gchar *uri)
 {
-       if (g_str_has_prefix (uri, "cid:")) {
-               EMailDisplay *display;
-               EMailPartList *part_list;
-               CamelMimeMessage *message;
-               CamelMimePart *mime_part;
-               const gchar *filename;
+       EMailPartList *part_list;
+       CamelMimeMessage *message;
+       CamelMimePart *mime_part;
 
-               /* Note, this assumes the URI comes
-                * from the currently loaded message. */
+       if (!g_str_has_prefix (uri, "cid:"))
+               return NULL;
 
-               display = E_MAIL_DISPLAY (web_view);
+       part_list = e_mail_display_get_part_list (display);
+       if (!part_list)
+               return NULL;
 
-               part_list = e_mail_display_get_part_list (display);
-               if (part_list == NULL)
-                       return NULL;
+       message = e_mail_part_list_get_message (part_list);
+       if (!message)
+               return NULL;
 
-               message = e_mail_part_list_get_message (part_list);
-               if (message == NULL)
-                       return NULL;
+       mime_part = camel_mime_message_get_part_by_content_id (
+               message, uri + 4);
 
-               mime_part = camel_mime_message_get_part_by_content_id (
-                       message, uri + 4);
-               if (mime_part == NULL)
-                       return NULL;
+       return mime_part;
+}
 
-               filename = camel_mime_part_get_filename (mime_part);
+static gchar *
+mail_display_suggest_filename (EWebView *web_view,
+                               const gchar *uri)
+{
+       EMailDisplay *display;
+       CamelMimePart *mime_part;
 
-               return g_strdup (filename);
-       }
+       /* Note, this assumes the URI comes
+        * from the currently loaded message. */
+       display = E_MAIL_DISPLAY (web_view);
+
+       mime_part = camel_mime_part_from_cid (display, uri);
+
+       if (mime_part)
+               return g_strdup (camel_mime_part_get_filename (mime_part));
 
        /* Chain up to parent's suggest_filename() method. */
        return E_WEB_VIEW_CLASS (e_mail_display_parent_class)->
@@ -1446,6 +1453,68 @@ e_mail_display_test_change_and_update_fonts_cb (EMailDisplay *mail_display,
 }
 
 static void
+mail_display_drag_data_get (GtkWidget *widget,
+                            GdkDragContext *context,
+                            GtkSelectionData *data,
+                            guint info,
+                            guint time,
+                            EMailDisplay *display)
+{
+       CamelDataWrapper *dw;
+       CamelMimePart *mime_part;
+       CamelStream *stream;
+       gchar *src, *base64_encoded, *mime_type, *uri;
+       const gchar *filename;
+       const guchar *data_from_webkit;
+       gint length;
+       GByteArray *byte_array;
+
+       data_from_webkit = gtk_selection_data_get_data (data);
+       length = gtk_selection_data_get_length (data);
+
+       uri = g_strndup ((const gchar *) data_from_webkit, length);
+
+       mime_part = camel_mime_part_from_cid (display, uri);
+
+       if (!mime_part)
+               goto out;
+
+       stream = camel_stream_mem_new ();
+       dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+       g_return_if_fail (dw);
+
+       mime_type = camel_data_wrapper_get_mime_type (dw);
+       camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL);
+       camel_stream_close (stream, NULL, NULL);
+
+       byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
+
+       if (!byte_array->data) {
+               g_object_unref (stream);
+               goto out;
+       }
+
+       base64_encoded = g_base64_encode ((const guchar *) byte_array->data, byte_array->len);
+
+       filename = camel_mime_part_get_filename (mime_part);
+       /* Insert filename before base64 data */
+       src = g_strconcat (filename, ";data:", mime_type, ";base64,", base64_encoded, NULL);
+
+       gtk_selection_data_set (
+               data,
+               gtk_selection_data_get_data_type (data),
+               gtk_selection_data_get_format (data),
+               (const guchar *) src, strlen (src));
+
+       g_free (src);
+       g_free (base64_encoded);
+       g_free (mime_type);
+       g_object_unref (stream);
+ out:
+       g_free (uri);
+}
+
+static void
 e_mail_display_class_init (EMailDisplayClass *class)
 {
        GObjectClass *object_class;
@@ -1575,6 +1644,9 @@ e_mail_display_init (EMailDisplay *display)
        g_signal_connect (
                display, "document-load-finished",
                G_CALLBACK (initialize_web_view_colors), NULL);
+       g_signal_connect_after (
+               display, "drag-data-get",
+               G_CALLBACK (mail_display_drag_data_get), display);
 
        display->priv->settings = g_settings_new ("org.gnome.evolution.mail");
        g_signal_connect_swapped (
diff --git a/mail/e-mail-reader-utils.c b/mail/e-mail-reader-utils.c
index 07ac192..f1a6126 100644
--- a/mail/e-mail-reader-utils.c
+++ b/mail/e-mail-reader-utils.c
@@ -28,7 +28,6 @@
 
 #include <glib/gi18n.h>
 #include <libxml/tree.h>
-#include <gtkhtml/gtkhtml.h>
 #include <camel/camel.h>
 
 #include <shell/e-shell-utils.h>
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index cbc595a..46c4cf9 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -2464,7 +2464,7 @@ mail_reader_key_press_event_cb (EMailReader *reader,
                if (frame != NULL) {
                        dom = webkit_web_frame_get_dom_document (frame);
                        /* intentionally used "static_cast" */
-                       element = webkit_dom_html_document_get_active_element ((WebKitDOMHTMLDocument *) dom);
+                       element = webkit_dom_html_document_get_active_element (WEBKIT_DOM_HTML_DOCUMENT 
(dom));
 
                        if (element != NULL)
                                name = webkit_dom_node_get_node_name (WEBKIT_DOM_NODE (element));
diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c
index 22cc594..afa7e28 100644
--- a/mail/em-composer-utils.c
+++ b/mail/em-composer-utils.c
@@ -296,9 +296,11 @@ composer_presend_check_recipients (EMsgComposer *composer,
 
        /* I'm sensing a lack of love, er, I mean recipients. */
        if (num == 0 && num_post == 0) {
-               e_alert_submit (
-                       E_ALERT_SINK (composer),
-                       "mail:send-no-recipients", NULL);
+               EHTMLEditor *editor;
+
+               editor = e_msg_composer_get_editor (composer);
+               e_alert_submit (E_ALERT_SINK (editor), "mail:send-no-recipients", NULL);
+
                goto finished;
        }
 
@@ -443,6 +445,8 @@ composer_presend_check_unwanted_html (EMsgComposer *composer,
                                       EMailSession *session)
 {
        EDestination **recipients;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        EComposerHeaderTable *table;
        GSettings *settings;
        gboolean check_passed = TRUE;
@@ -453,9 +457,12 @@ composer_presend_check_unwanted_html (EMsgComposer *composer,
 
        settings = g_settings_new ("org.gnome.evolution.mail");
 
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+       html_mode = e_html_editor_view_get_html_mode (view);
+
        table = e_msg_composer_get_header_table (composer);
        recipients = e_composer_header_table_get_destinations (table);
-       html_mode = gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer));
 
        send_html = g_settings_get_boolean (settings, "composer-send-html");
        confirm_html = g_settings_get_boolean (settings, "prompt-on-unwanted-html");
@@ -585,8 +592,13 @@ exit:
        g_clear_error (&local_error);
 
        if (set_changed) {
-               gtkhtml_editor_set_changed (
-                       GTKHTML_EDITOR (async_context->composer), TRUE);
+               EHTMLEditor *editor;
+               EHTMLEditorView *view;
+
+               editor = e_msg_composer_get_editor (async_context->composer);
+               view = e_html_editor_get_view (editor);
+               e_html_editor_view_set_changed (view, TRUE);
+
                gtk_window_present (GTK_WINDOW (async_context->composer));
        }
 
@@ -638,14 +650,15 @@ em_utils_composer_send_cb (EMsgComposer *composer,
 static void
 composer_set_no_change (EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
        g_return_if_fail (composer != NULL);
 
-       editor = GTKHTML_EDITOR (composer);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
 
-       gtkhtml_editor_drop_undo (editor);
-       gtkhtml_editor_set_changed (editor, FALSE);
+       e_html_editor_view_set_changed (view, FALSE);
 }
 
 /* delete original messages from Outbox folder */
@@ -690,8 +703,15 @@ composer_save_to_drafts_complete (GObject *source_object,
 {
        EActivity *activity;
        AsyncContext *async_context;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        GError *local_error = NULL;
 
+       async_context = (AsyncContext *) user_data;
+
+       editor = e_msg_composer_get_editor (async_context->composer);
+       view = e_html_editor_get_view (editor);
+
        /* We don't really care if this failed.  If something other than
         * cancellation happened, emit a runtime warning so the error is
         * not completely lost. */
@@ -704,13 +724,11 @@ composer_save_to_drafts_complete (GObject *source_object,
                E_MAIL_SESSION (source_object), result, &local_error);
 
        if (e_activity_handle_cancellation (activity, local_error)) {
-               gtkhtml_editor_set_changed (
-                       GTKHTML_EDITOR (async_context->composer), TRUE);
+               e_html_editor_view_set_changed (view, TRUE);
                g_error_free (local_error);
 
        } else if (local_error != NULL) {
-               gtkhtml_editor_set_changed (
-                       GTKHTML_EDITOR (async_context->composer), TRUE);
+               e_html_editor_view_set_changed (view, TRUE);
                g_warning ("%s", local_error->message);
                g_error_free (local_error);
 
@@ -738,35 +756,32 @@ composer_save_to_drafts_cleanup (GObject *source_object,
        EActivity *activity;
        EAlertSink *alert_sink;
        GCancellable *cancellable;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        AsyncContext *async_context;
        GError *local_error = NULL;
 
        async_context = (AsyncContext *) user_data;
 
+       editor = e_msg_composer_get_editor (async_context->composer);
+       view = e_html_editor_get_view (editor);
+
        activity = async_context->activity;
        alert_sink = e_activity_get_alert_sink (activity);
        cancellable = e_activity_get_cancellable (activity);
 
-       e_mail_folder_append_message_finish (
-               CAMEL_FOLDER (source_object), result,
-               &async_context->message_uid, &local_error);
-
        if (e_activity_handle_cancellation (activity, local_error)) {
-               g_warn_if_fail (async_context->message_uid == NULL);
-               gtkhtml_editor_set_changed (
-                       GTKHTML_EDITOR (async_context->composer), TRUE);
+               e_html_editor_view_set_changed (view, TRUE);
                async_context_free (async_context);
                g_error_free (local_error);
                return;
 
        } else if (local_error != NULL) {
-               g_warn_if_fail (async_context->message_uid == NULL);
                e_alert_submit (
                        alert_sink,
                        "mail-composer:save-to-drafts-error",
                        local_error->message, NULL);
-               gtkhtml_editor_set_changed (
-                       GTKHTML_EDITOR (async_context->composer), TRUE);
+               e_html_editor_view_set_changed (view, TRUE);
                async_context_free (async_context);
                g_error_free (local_error);
                return;
@@ -830,6 +845,8 @@ composer_save_to_drafts_got_folder (GObject *source_object,
 {
        EActivity *activity;
        CamelFolder *drafts_folder;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        AsyncContext *async_context;
        GError *local_error = NULL;
 
@@ -837,6 +854,9 @@ composer_save_to_drafts_got_folder (GObject *source_object,
 
        activity = async_context->activity;
 
+       editor = e_msg_composer_get_editor (async_context->composer);
+       view = e_html_editor_get_view (editor);
+
        drafts_folder = e_mail_session_uri_to_folder_finish (
                E_MAIL_SESSION (source_object), result, &local_error);
 
@@ -846,8 +866,7 @@ composer_save_to_drafts_got_folder (GObject *source_object,
                ((drafts_folder == NULL) && (local_error != NULL)));
 
        if (e_activity_handle_cancellation (activity, local_error)) {
-               gtkhtml_editor_set_changed (
-                       GTKHTML_EDITOR (async_context->composer), TRUE);
+               e_html_editor_view_set_changed (view, TRUE);
                async_context_free (async_context);
                g_error_free (local_error);
                return;
@@ -865,8 +884,7 @@ composer_save_to_drafts_got_folder (GObject *source_object,
                        GTK_WINDOW (async_context->composer),
                        "mail:ask-default-drafts", NULL);
                if (response != GTK_RESPONSE_YES) {
-                       gtkhtml_editor_set_changed (
-                               GTKHTML_EDITOR (async_context->composer), TRUE);
+                       e_html_editor_view_set_changed (view, TRUE);
                        async_context_free (async_context);
                        return;
                }
@@ -1155,6 +1173,7 @@ em_utils_compose_new_message (EShell *shell,
 
        composer = create_new_composer (shell, "", folder);
        composer_set_no_change (composer);
+       e_msg_composer_is_from_new_message (composer, TRUE);
 
        gtk_widget_show (GTK_WIDGET (composer));
 
@@ -1770,7 +1789,7 @@ forward_non_attached (EMailBackend *backend,
        forward = quoting_text (QUOTING_FORWARD);
        text = em_utils_message_to_html (
                CAMEL_SESSION (session), message,
-               forward, flags, NULL, NULL, &validity_found);
+               forward, flags, NULL, NULL, NULL, &validity_found);
 
        if (text != NULL) {
                CamelDataWrapper *content;
@@ -2818,16 +2837,10 @@ composer_set_body (EMsgComposer *composer,
        gchar *text, *credits, *original;
        CamelMimePart *part;
        CamelSession *session;
-       GSettings *settings;
-       gboolean start_bottom, has_body_text = FALSE;
        guint32 validity_found = 0;
 
        session = e_msg_composer_ref_session (composer);
 
-       settings = g_settings_new ("org.gnome.evolution.mail");
-
-       start_bottom = g_settings_get_boolean (settings, "composer-reply-start-bottom");
-
        switch (style) {
        case E_MAIL_REPLY_STYLE_DO_NOT_QUOTE:
                /* do nothing */
@@ -2842,9 +2855,9 @@ composer_set_body (EMsgComposer *composer,
                original = quoting_text (QUOTING_ORIGINAL);
                text = em_utils_message_to_html (
                        session, message, original, E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS,
-                       parts_list, start_bottom ? "<BR>" : NULL, &validity_found);
+                       parts_list, "<span id=\"-x-evolution-reply-citation\">",
+                       "</span>", &validity_found);
                e_msg_composer_set_body_text (composer, text, TRUE);
-               has_body_text = text && *text;
                g_free (text);
                g_free (original);
                emu_update_composers_security (composer, validity_found);
@@ -2856,41 +2869,15 @@ composer_set_body (EMsgComposer *composer,
                credits = attribution_format (message);
                text = em_utils_message_to_html (
                        session, message, credits, E_MAIL_FORMATTER_QUOTE_FLAG_CITE,
-                       parts_list, start_bottom ? "<BR>" : NULL, &validity_found);
+                       parts_list, "<span id=\"-x-evolution-reply-citation\">",
+                       "</span>", &validity_found);
                g_free (credits);
                e_msg_composer_set_body_text (composer, text, TRUE);
-               has_body_text = text && *text;
                g_free (text);
                emu_update_composers_security (composer, validity_found);
                break;
        }
 
-       if (has_body_text && start_bottom) {
-               GtkhtmlEditor *editor = GTKHTML_EDITOR (composer);
-               gboolean move_cursor_to_end;
-               gboolean top_signature;
-
-               /* If we are placing signature on top, then move cursor to the end,
-                * otherwise try to find the signature place and place cursor just
-                * before the signature. We added there an empty line already. */
-               gtkhtml_editor_run_command (editor, "block-selection");
-               gtkhtml_editor_run_command (editor, "cursor-bod");
-
-               top_signature = g_settings_get_boolean (settings, "composer-top-signature");
-
-               move_cursor_to_end = top_signature ||
-                       !gtkhtml_editor_search_by_data (
-                               editor, 1, "ClueFlow", "signature", "1");
-
-               if (move_cursor_to_end)
-                       gtkhtml_editor_run_command (editor, "cursor-eod");
-               else
-                       gtkhtml_editor_run_command (editor, "selection-move-left");
-               gtkhtml_editor_run_command (editor, "unblock-selection");
-       }
-
-       g_object_unref (settings);
-
        g_object_unref (session);
 }
 
@@ -2900,13 +2887,14 @@ em_utils_construct_composer_text (CamelSession *session,
                                   EMailPartList *parts_list)
 {
        gchar *text, *credits;
+       gboolean start_bottom = FALSE;
 
        g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
 
        credits = attribution_format (message);
        text = em_utils_message_to_html (
                session, message, credits, E_MAIL_FORMATTER_QUOTE_FLAG_CITE,
-               parts_list, NULL, NULL);
+               parts_list, NULL, start_bottom ? "<BR>" : NULL, NULL);
        g_free (credits);
 
        return text;
diff --git a/mail/em-utils.c b/mail/em-utils.c
index 2291e08..f1f23d3 100644
--- a/mail/em-utils.c
+++ b/mail/em-utils.c
@@ -1191,6 +1191,7 @@ em_utils_message_to_html (CamelSession *session,
                           const gchar *credits,
                           guint32 flags,
                           EMailPartList *parts_list,
+                          const gchar *prepend,
                           const gchar *append,
                           EMailPartValidityFlags *validity_found)
 {
@@ -1265,6 +1266,10 @@ em_utils_message_to_html (CamelSession *session,
        if (validity_found != NULL)
                *validity_found = is_validity_found;
 
+       if (prepend != NULL && *prepend != '\0')
+               g_output_stream_write_all (
+                       stream, prepend, strlen (prepend), NULL, NULL, NULL);
+
        e_mail_formatter_format_sync (
                formatter, parts_list, stream, 0,
                E_MAIL_FORMATTER_MODE_PRINTING, NULL);
diff --git a/mail/em-utils.h b/mail/em-utils.h
index 6725a06..907a91b 100644
--- a/mail/em-utils.h
+++ b/mail/em-utils.h
@@ -64,6 +64,7 @@ gchar *               em_utils_message_to_html        (CamelSession *session,
                                                 const gchar *credits,
                                                 guint32 flags,
                                                 struct _EMailPartList *parts_list,
+                                                const gchar *prepend,
                                                 const gchar *append,
                                                 EMailPartValidityFlags *validity_found);
 
diff --git a/mail/importers/Makefile.am b/mail/importers/Makefile.am
index a1843c5..d9beee9 100644
--- a/mail/importers/Makefile.am
+++ b/mail/importers/Makefile.am
@@ -9,8 +9,8 @@ libevolution_mail_importers_la_CPPFLAGS = \
        -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\"    \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 libevolution_mail_importers_la_SOURCES =       \
        mail-importer.c                         \
@@ -28,6 +28,6 @@ libevolution_mail_importers_la_LIBADD =                               \
        $(top_builddir)/libemail-engine/libemail-engine.la      \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 -include $(top_srcdir)/git.mk
diff --git a/mail/mail-config.ui b/mail/mail-config.ui
index ddc5b89..7b7e7c1 100644
--- a/mail/mail-config.ui
+++ b/mail/mail-config.ui
@@ -173,6 +173,13 @@
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
+  <object class="GtkAdjustment" id="adjustment7">
+    <property name="upper">200</property>
+    <property name="lower">31</property>
+    <property name="value">71</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">1</property>
+  </object>
   <object class="GtkNotebook" id="composer_toplevel">
     <property name="visible">True</property>
     <property name="can_focus">True</property>
@@ -294,6 +301,49 @@
                       </packing>
                     </child>
                     <child>
+                      <object class="GtkHBox" id="hboxWrapCharactersCount">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="lblWordWrapLength">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Number of characters for word 
w_rapping:</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="spinWordWrapLength">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="max_length">3</property>
+                            <property name="adjustment">adjustment7</property>
+                            <property name="caps_lock_warning">False</property>
+                            <property name="numeric">True</property>
+                            <property name="update_policy">if-valid</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">5</property>
+                      </packing>
+                    </child>
+                    <child>
                       <object class="GtkHBox" id="hboxComposerCharset">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
@@ -320,7 +370,7 @@
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
-                        <property name="position">5</property>
+                        <property name="position">6</property>
                       </packing>
                     </child>
                   </object>
@@ -797,47 +847,6 @@
                         <property name="position">0</property>
                       </packing>
                     </child>
-                    <child>
-                      <object class="GtkHBox" id="hboxSpellCheckColor">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="spacing">6</property>
-                        <child>
-                          <object class="GtkLabel" id="lblSpellCheckColor">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="label" translatable="yes">Color for _misspelled words:</property>
-                            <property name="use_underline">True</property>
-                            <property name="justify">center</property>
-                            <property name="mnemonic_widget">colorButtonSpellCheckColor</property>
-                          </object>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkColorButton" id="colorButtonSpellCheckColor">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">True</property>
-                            <property name="title" translatable="yes">Pick a color</property>
-                            <property name="color">#000000000000</property>
-                          </object>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
                   </object>
                 </child>
               </object>
diff --git a/modules/addressbook/Makefile.am b/modules/addressbook/Makefile.am
index fc2a562..d84247a 100644
--- a/modules/addressbook/Makefile.am
+++ b/modules/addressbook/Makefile.am
@@ -19,9 +19,9 @@ module_addressbook_la_CPPFLAGS = \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
        $(CHAMPLAIN_CFLAGS)                                     \
-       $(GTKHTML_CFLAGS)                                       \
        $(LDAP_CFLAGS)                                          \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_addressbook_la_SOURCES = \
        evolution-module-addressbook.c                          \
@@ -64,8 +64,8 @@ module_addressbook_la_LIBADD = \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
        $(CHAMPLAIN_LIBS)                                       \
-       $(GTKHTML_LIBS)                                         \
-       $(LDAP_LIBS)
+       $(LDAP_LIBS)                                            \
+       $(NULL)
 
 
 module_addressbook_la_LDFLAGS = \
diff --git a/modules/backup-restore/Makefile.am b/modules/backup-restore/Makefile.am
index 98d1301..9dffc4a 100644
--- a/modules/backup-restore/Makefile.am
+++ b/modules/backup-restore/Makefile.am
@@ -14,7 +14,6 @@ module_backup_restore_la_CPPFLAGS = \
        -DLIBDIR=\""$(libdir)"\"                                \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(CODE_COVERAGE_CFLAGS)                                 \
        $(NULL)
 
@@ -33,7 +32,6 @@ module_backup_restore_la_LIBADD = \
        $(top_builddir)/libemail-engine/libemail-engine.la      \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
        $(NULL)
 
 module_backup_restore_la_LDFLAGS = \
@@ -53,7 +51,6 @@ evolution_backup_CPPFLAGS =                                   \
        -DDBUS_SERVICES_DIR=\"'${datadir}'/dbus-1/services\"    \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(NULL)
 
 evolution_backup_SOURCES =                                     \
@@ -64,7 +61,6 @@ evolution_backup_LDADD =                                      \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
        $(NULL)
 
 if OS_WIN32
diff --git a/modules/backup-restore/evolution-backup-tool.c b/modules/backup-restore/evolution-backup-tool.c
index 4b75f8a..728b8f9 100644
--- a/modules/backup-restore/evolution-backup-tool.c
+++ b/modules/backup-restore/evolution-backup-tool.c
@@ -101,38 +101,6 @@ static GOptionEntry options[] = {
 
 static gboolean check (const gchar *filename, gboolean *is_new_format);
 
-static GString *
-replace_string (const gchar *text,
-                const gchar *find,
-                const gchar *replace)
-{
-       const gchar *p, *next;
-       GString *str;
-       gint find_len;
-
-       g_return_val_if_fail (text != NULL, NULL);
-       g_return_val_if_fail (find != NULL, NULL);
-       g_return_val_if_fail (*find, NULL);
-
-       find_len = strlen (find);
-       str = g_string_new ("");
-
-       p = text;
-       while (next = strstr (p, find), next) {
-               if (p < next)
-                       g_string_append_len (str, p, next - p);
-
-               if (replace && *replace)
-                       g_string_append (str, replace);
-
-               p = next + find_len;
-       }
-
-       g_string_append (str, p);
-
-       return str;
-}
-
 static const gchar *
 strip_home_dir (const gchar *dir)
 {
@@ -166,11 +134,11 @@ replace_variables (const gchar *str,
        strip_datadir = strip_home_dir (e_get_user_data_dir ());
        strip_configdir = strip_home_dir (e_get_user_config_dir ());
 
-       #define repl(_find, _replace) \
-               use = replace_string (res ? res->str : str, _find, _replace); \
-               g_return_val_if_fail (use != NULL, NULL); \
-               if (res) \
-                       g_string_free (res, TRUE); \
+       #define repl(_find, _replace)                                                   \
+               use = e_str_replace_string (res ? res->str : str, _find, _replace);     \
+               g_return_val_if_fail (use != NULL, NULL);                               \
+               if (res)                                                                \
+                       g_string_free (res, TRUE);                                      \
                res = use;
 
        repl ("$HOME", g_get_home_dir ());
@@ -223,7 +191,7 @@ replace_in_file (const gchar *filename,
        }
 
        if (g_file_get_contents (filename, &content, NULL, &error)) {
-               GString *str = replace_string (content, find, replace);
+               GString *str = e_str_replace_string (content, find, replace);
 
                if (str) {
                        if (!g_file_set_contents (filename, str->str, -1, &error) && error) {
diff --git a/modules/book-config-google/Makefile.am b/modules/book-config-google/Makefile.am
index 0de66dc..cdd75f7 100644
--- a/modules/book-config-google/Makefile.am
+++ b/modules/book-config-google/Makefile.am
@@ -6,8 +6,8 @@ module_book_config_google_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-book-config-google\"         \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_book_config_google_la_SOURCES = \
        evolution-book-config-google.c
@@ -16,7 +16,7 @@ module_book_config_google_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_book_config_google_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/book-config-ldap/Makefile.am b/modules/book-config-ldap/Makefile.am
index aa2bdb8..7f73e4c 100644
--- a/modules/book-config-ldap/Makefile.am
+++ b/modules/book-config-ldap/Makefile.am
@@ -6,9 +6,9 @@ module_book_config_ldap_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-book-config-ldap\"           \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(LDAP_CFLAGS)                                          \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_book_config_ldap_la_SOURCES = \
        evolution-book-config-ldap.c                            \
@@ -19,8 +19,8 @@ module_book_config_ldap_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
-       $(LDAP_LIBS)
+       $(LDAP_LIBS)                                            \
+       $(NULL)
 
 module_book_config_ldap_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/book-config-local/Makefile.am b/modules/book-config-local/Makefile.am
index 8c1d26b..f7442be 100644
--- a/modules/book-config-local/Makefile.am
+++ b/modules/book-config-local/Makefile.am
@@ -6,8 +6,8 @@ module_book_config_local_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-book-config-local\"          \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_book_config_local_la_SOURCES = \
        evolution-book-config-local.c
@@ -16,7 +16,7 @@ module_book_config_local_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_book_config_local_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/book-config-webdav/Makefile.am b/modules/book-config-webdav/Makefile.am
index 25a8f00..7e95f6f 100644
--- a/modules/book-config-webdav/Makefile.am
+++ b/modules/book-config-webdav/Makefile.am
@@ -6,8 +6,8 @@ module_book_config_webdav_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-book-config-webdav\"         \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_book_config_webdav_la_SOURCES = \
        evolution-book-config-webdav.c
@@ -16,7 +16,7 @@ module_book_config_webdav_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_book_config_webdav_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-caldav/Makefile.am b/modules/cal-config-caldav/Makefile.am
index 03126c4..95ffc2a 100644
--- a/modules/cal-config-caldav/Makefile.am
+++ b/modules/cal-config-caldav/Makefile.am
@@ -6,9 +6,9 @@ module_cal_config_caldav_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-cal-config-caldav\"          \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(LIBSOUP_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_cal_config_caldav_la_SOURCES = \
        evolution-cal-config-caldav.c                           \
@@ -21,8 +21,8 @@ module_cal_config_caldav_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
-       $(LIBSOUP_LIBS)
+       $(LIBSOUP_LIBS)                                         \
+       $(NULL)
 
 module_cal_config_caldav_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-contacts/Makefile.am b/modules/cal-config-contacts/Makefile.am
index 4a1e391..2c3cc4f 100644
--- a/modules/cal-config-contacts/Makefile.am
+++ b/modules/cal-config-contacts/Makefile.am
@@ -6,8 +6,8 @@ module_cal_config_contacts_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-cal-config-contacts\"        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_cal_config_contacts_la_SOURCES = \
        evolution-cal-config-contacts.c                         \
@@ -20,7 +20,7 @@ module_cal_config_contacts_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_cal_config_contacts_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-google/Makefile.am b/modules/cal-config-google/Makefile.am
index 5ebdf56..2f1a3de 100644
--- a/modules/cal-config-google/Makefile.am
+++ b/modules/cal-config-google/Makefile.am
@@ -6,9 +6,9 @@ module_cal_config_google_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-cal-config-google\"          \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(GDATA_CFLAGS)                                         \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_cal_config_google_la_SOURCES = \
        evolution-cal-config-google.c                           \
@@ -23,8 +23,8 @@ module_cal_config_google_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
-       $(GDATA_LIBS)
+       $(GDATA_LIBS)                                           \
+       $(NULL)
 
 module_cal_config_google_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-local/Makefile.am b/modules/cal-config-local/Makefile.am
index 2d4d1a0..c503a02 100644
--- a/modules/cal-config-local/Makefile.am
+++ b/modules/cal-config-local/Makefile.am
@@ -6,8 +6,8 @@ module_cal_config_local_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-cal-config-local\"           \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_cal_config_local_la_SOURCES = \
        evolution-cal-config-local.c                            \
@@ -18,7 +18,7 @@ module_cal_config_local_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_cal_config_local_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-weather/Makefile.am b/modules/cal-config-weather/Makefile.am
index f1a22f0..54c4850 100644
--- a/modules/cal-config-weather/Makefile.am
+++ b/modules/cal-config-weather/Makefile.am
@@ -6,9 +6,9 @@ module_cal_config_weather_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-cal-config-weather\"         \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(GWEATHER_CFLAGS)                                      \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_cal_config_weather_la_SOURCES = \
        evolution-cal-config-weather.c                          \
@@ -19,8 +19,8 @@ module_cal_config_weather_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
-       $(GWEATHER_LIBS)
+       $(GWEATHER_LIBS)                                        \
+       $(NULL)
 
 module_cal_config_weather_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/cal-config-webcal/Makefile.am b/modules/cal-config-webcal/Makefile.am
index 43c0d25..36cacf6 100644
--- a/modules/cal-config-webcal/Makefile.am
+++ b/modules/cal-config-webcal/Makefile.am
@@ -6,8 +6,8 @@ module_cal_config_webcal_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-cal-config-webcal\"          \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_cal_config_webcal_la_SOURCES = \
        evolution-cal-config-webcal.c
@@ -16,7 +16,7 @@ module_cal_config_webcal_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_cal_config_webcal_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/calendar/Makefile.am b/modules/calendar/Makefile.am
index 348da1b..ce02506 100644
--- a/modules/calendar/Makefile.am
+++ b/modules/calendar/Makefile.am
@@ -9,8 +9,8 @@ module_calendar_la_CPPFLAGS = \
        -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\"        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 module_calendar_la_SOURCES = \
        evolution-module-calendar.c                     \
@@ -77,7 +77,7 @@ module_calendar_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la                     \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_calendar_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/composer-autosave/Makefile.am b/modules/composer-autosave/Makefile.am
index a3c16be..10cae2d 100644
--- a/modules/composer-autosave/Makefile.am
+++ b/modules/composer-autosave/Makefile.am
@@ -1,34 +1,29 @@
-NULL =
-
 module_LTLIBRARIES = module-composer-autosave.la
 
 module_composer_autosave_la_CPPFLAGS = \
-       $(AM_CPPFLAGS) \
-       -I$(top_srcdir) \
-       -DG_LOG_DOMAIN=\"evolution-composer-autosave\" \
-       $(EVOLUTION_DATA_SERVER_CFLAGS) \
-       $(GNOME_PLATFORM_CFLAGS) \
-       $(GTKHTML_CFLAGS) \
-       $(CODE_COVERAGE_CFLAGS) \
+       $(AM_CPPFLAGS)                                          \
+       -I$(top_srcdir)                                         \
+       -DG_LOG_DOMAIN=\"evolution-composer-autosave\"          \
+       $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
+       $(GNOME_PLATFORM_CFLAGS)                                \
+       $(CODE_COVERAGE_CFLAGS)                                 \
        $(NULL)
 
 module_composer_autosave_la_SOURCES = \
-       evolution-composer-autosave.c \
-       e-autosave-utils.c \
-       e-autosave-utils.h \
-       e-composer-autosave.c \
-       e-composer-autosave.h \
-       e-composer-registry.c \
-       e-composer-registry.h \
-       $(NULL)
+       evolution-composer-autosave.c                           \
+       e-autosave-utils.c                                      \
+       e-autosave-utils.h                                      \
+       e-composer-autosave.c                                   \
+       e-composer-autosave.h                                   \
+       e-composer-registry.c                                   \
+       e-composer-registry.h
 
 module_composer_autosave_la_LIBADD = \
-       $(top_builddir)/shell/libevolution-shell.la \
-       $(top_builddir)/composer/libevolution-mail-composer.la \
-       $(top_builddir)/e-util/libevolution-util.la \
-       $(EVOLUTION_DATA_SERVER_LIBS) \
-       $(GNOME_PLATFORM_LIBS) \
-       $(GTKHTML_LIBS) \
+       $(top_builddir)/shell/libevolution-shell.la             \
+       $(top_builddir)/composer/libevolution-mail-composer.la  \
+       $(top_builddir)/e-util/libevolution-util.la             \
+       $(EVOLUTION_DATA_SERVER_LIBS)                           \
+       $(GNOME_PLATFORM_LIBS)                                  \
        $(NULL)
 
 module_composer_autosave_la_LDFLAGS = \
diff --git a/modules/composer-autosave/e-composer-autosave.c b/modules/composer-autosave/e-composer-autosave.c
index 33d6c02..d75532c 100644
--- a/modules/composer-autosave/e-composer-autosave.c
+++ b/modules/composer-autosave/e-composer-autosave.c
@@ -125,13 +125,15 @@ composer_autosave_timeout_cb (gpointer user_data)
 static void
 composer_autosave_changed_cb (EComposerAutosave *autosave)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        EExtensible *extensible;
 
        extensible = e_extension_get_extensible (E_EXTENSION (autosave));
 
-       editor = GTKHTML_EDITOR (extensible);
-       autosave->priv->changed = gtkhtml_editor_get_changed (editor);
+       editor = e_msg_composer_get_editor (E_MSG_COMPOSER (extensible));
+       view = e_html_editor_get_view (editor);
+       autosave->priv->changed = e_html_editor_view_get_changed (view);
 
        if (autosave->priv->changed && autosave->priv->timeout_id == 0) {
                autosave->priv->timeout_id = e_named_timeout_add_seconds (
@@ -164,6 +166,8 @@ composer_autosave_dispose (GObject *object)
 static void
 composer_autosave_constructed (GObject *object)
 {
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        EExtensible *extensible;
 
        /* Chain up to parent's constructed() method. */
@@ -171,9 +175,11 @@ composer_autosave_constructed (GObject *object)
                constructed (object);
 
        extensible = e_extension_get_extensible (E_EXTENSION (object));
+       editor = e_msg_composer_get_editor (E_MSG_COMPOSER (extensible));
+       view = e_html_editor_get_view (editor);
 
        e_signal_connect_notify_swapped (
-               extensible, "notify::changed",
+               view, "notify::changed",
                G_CALLBACK (composer_autosave_changed_cb), object);
 }
 
diff --git a/modules/contact-photos/Makefile.am b/modules/contact-photos/Makefile.am
index 100ed9a..5f2b19d 100644
--- a/modules/contact-photos/Makefile.am
+++ b/modules/contact-photos/Makefile.am
@@ -6,7 +6,6 @@ module_contact_photos_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-contact-photos\"             \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(CODE_COVERAGE_CFLAGS)                                 \
        $(NULL)
 
@@ -22,7 +21,6 @@ module_contact_photos_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
        $(NULL)
 
 module_contact_photos_la_LDFLAGS = \
diff --git a/modules/gravatar/Makefile.am b/modules/gravatar/Makefile.am
index 874a403..4613ea9 100644
--- a/modules/gravatar/Makefile.am
+++ b/modules/gravatar/Makefile.am
@@ -6,7 +6,6 @@ module_gravatar_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-gravatar\"                   \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(CODE_COVERAGE_CFLAGS)                                 \
        $(NULL)
 
@@ -22,7 +21,6 @@ module_gravatar_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
        $(NULL)
 
 module_gravatar_la_LDFLAGS = \
diff --git a/modules/itip-formatter/Makefile.am b/modules/itip-formatter/Makefile.am
index 6d7e62f..2e1168b 100644
--- a/modules/itip-formatter/Makefile.am
+++ b/modules/itip-formatter/Makefile.am
@@ -11,8 +11,8 @@ module_itip_formatter_la_CPPFLAGS =                                   \
        -DG_LOG_DOMAIN=\"evolution-module-itip-formatter\"              \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                                 \
        $(GNOME_PLATFORM_CFLAGS)                                        \
-       $(GTKHTML_CFLAGS)                                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                         \
+       $(NULL)
 
 module_itip_formatter_la_SOURCES =                                     \
        e-conflict-search-selector.c                                    \
@@ -38,7 +38,7 @@ module_itip_formatter_la_LIBADD =                                     \
        $(top_builddir)/libemail-engine/libemail-engine.la              \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_itip_formatter_la_LDFLAGS =                                     \
        -avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/itip-formatter/itip-view.c b/modules/itip-formatter/itip-view.c
index f19c418..64d075e 100644
--- a/modules/itip-formatter/itip-view.c
+++ b/modules/itip-formatter/itip-view.c
@@ -791,13 +791,7 @@ alarm_check_toggled_cb (WebKitDOMHTMLInputElement *check1,
        WebKitDOMElement *check2;
        gchar *id;
 
-#if WEBKIT_CHECK_VERSION(2,2,0)  /* XXX should really be (2,1,something) */
-       id = webkit_dom_element_get_id (
-               WEBKIT_DOM_ELEMENT (check1));
-#else
-       id = webkit_dom_html_element_get_id (
-               WEBKIT_DOM_HTML_ELEMENT (check1));
-#endif
+       id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (check1));
 
        if (g_strcmp0 (id, CHECKBOX_INHERIT_ALARM)) {
                check2 = webkit_dom_document_get_element_by_id (
@@ -906,11 +900,7 @@ append_info_item_row (ItipView *view,
                WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
 
        id = g_strdup_printf ("%s_row_%d", table_id, item->id);
-#if WEBKIT_CHECK_VERSION(2,2,0)  /* XXX should really be (2,1,something) */
        webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (row), id);
-#else
-       webkit_dom_html_element_set_id (row, id);
-#endif
        g_free (id);
 
        switch (item->type) {
@@ -932,7 +922,7 @@ append_info_item_row (ItipView *view,
        }
 
        cell = webkit_dom_html_table_row_element_insert_cell (
-               (WebKitDOMHTMLTableRowElement *) row, -1, NULL);
+               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
 
        if (icon_name) {
                WebKitDOMElement *image;
@@ -953,7 +943,7 @@ append_info_item_row (ItipView *view,
        }
 
        cell = webkit_dom_html_table_row_element_insert_cell (
-               (WebKitDOMHTMLTableRowElement *) row, -1, NULL);
+               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
 
        webkit_dom_html_element_set_inner_html (cell, item->message, NULL);
 
diff --git a/modules/itip-formatter/plugin/Makefile.am b/modules/itip-formatter/plugin/Makefile.am
index ac23084..30c75b8 100644
--- a/modules/itip-formatter/plugin/Makefile.am
+++ b/modules/itip-formatter/plugin/Makefile.am
@@ -10,8 +10,8 @@ liborg_gnome_itip_formatter_la_CPPFLAGS =             \
        -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\"    \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_itip_formatter_la_SOURCES =               \
        config-ui.c                                     \
@@ -30,7 +30,7 @@ liborg_gnome_itip_formatter_la_LIBADD =                               \
        $(top_builddir)/em-format/libevolution-mail-formatter.la \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 BUILT_SOURCES = $(plugin_DATA)
 
diff --git a/modules/mail-config/Makefile.am b/modules/mail-config/Makefile.am
index 1a84b9e..c1d9eae 100644
--- a/modules/mail-config/Makefile.am
+++ b/modules/mail-config/Makefile.am
@@ -8,7 +8,6 @@ module_mail_config_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-mail-config\"                \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(CODE_COVERAGE_CFLAGS)                                 \
        $(NULL)
 
@@ -32,7 +31,6 @@ module_mail_config_la_LIBADD = \
        $(top_builddir)/libemail-engine/libemail-engine.la      \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
        $(NULL)
 
 module_mail_config_la_LDFLAGS = \
diff --git a/modules/mail/Makefile.am b/modules/mail/Makefile.am
index 595c357..770975e 100644
--- a/modules/mail/Makefile.am
+++ b/modules/mail/Makefile.am
@@ -9,8 +9,8 @@ module_mail_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-module-mail\"                        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                                 \
        $(GNOME_PLATFORM_CFLAGS)                                        \
-       $(GTKHTML_CFLAGS)                                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                         \
+       $(NULL)
 
 module_mail_la_SOURCES = \
        evolution-module-mail.c                                         \
@@ -51,7 +51,7 @@ module_mail_la_LIBADD = \
        $(libevolution_mail_settings_la)                                \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_mail_la_LDFLAGS = \
        -avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c
index e74086f..321e103 100644
--- a/modules/mail/e-mail-shell-backend.c
+++ b/modules/mail/e-mail-shell-backend.c
@@ -475,21 +475,26 @@ mail_shell_backend_window_added_cb (GtkApplication *application,
        EShell *shell = E_SHELL (application);
        EMailBackend *backend;
        EMailSession *session;
+       EHTMLEditor *editor = NULL;
        const gchar *backend_name;
 
        backend = E_MAIL_BACKEND (shell_backend);
        session = e_mail_backend_get_session (backend);
 
+       if (E_IS_MSG_COMPOSER (window))
+               editor = e_msg_composer_get_editor (E_MSG_COMPOSER (window));
+
+       if (E_IS_MAIL_SIGNATURE_EDITOR (window))
+               editor = e_mail_signature_editor_get_editor (
+                       E_MAIL_SIGNATURE_EDITOR (window));
+
        /* This applies to both the composer and signature editor. */
-       if (GTKHTML_IS_EDITOR (window)) {
+       if (editor != NULL) {
+               EHTMLEditorView *view;
                GSettings *settings;
-               GList *spell_languages;
                gboolean active = TRUE;
 
-               spell_languages = e_load_spell_languages ();
-               gtkhtml_editor_set_spell_languages (
-                       GTKHTML_EDITOR (window), spell_languages);
-               g_list_free (spell_languages);
+               view = e_html_editor_get_view (editor);
 
                settings = g_settings_new ("org.gnome.evolution.mail");
 
@@ -498,7 +503,7 @@ mail_shell_backend_window_added_cb (GtkApplication *application,
 
                g_object_unref (settings);
 
-               gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (window), active);
+               e_html_editor_view_set_html_mode (view, active);
        }
 
        if (E_IS_MSG_COMPOSER (window)) {
diff --git a/modules/mail/e-mail-shell-view-private.c b/modules/mail/e-mail-shell-view-private.c
index aff084d..14b0830 100644
--- a/modules/mail/e-mail-shell-view-private.c
+++ b/modules/mail/e-mail-shell-view-private.c
@@ -265,8 +265,7 @@ mail_shell_view_mail_display_needs_key (EMailDisplay *mail_display,
                if (!frame)
                        return FALSE;
                dom = webkit_web_frame_get_dom_document (frame);
-               /* intentionally used "static_cast" */
-               element = webkit_dom_html_document_get_active_element ((WebKitDOMHTMLDocument *) dom);
+               element = webkit_dom_html_document_get_active_element (WEBKIT_DOM_HTML_DOCUMENT (dom));
 
                if (element)
                        name = webkit_dom_node_get_node_name (WEBKIT_DOM_NODE (element));
diff --git a/modules/mail/e-mail-shell-view-private.h b/modules/mail/e-mail-shell-view-private.h
index 2925f4e..e42222d 100644
--- a/modules/mail/e-mail-shell-view-private.h
+++ b/modules/mail/e-mail-shell-view-private.h
@@ -24,7 +24,6 @@
 #include "e-mail-shell-view.h"
 
 #include <glib/gi18n.h>
-#include <gtkhtml/gtkhtml.h>
 #include <camel/camel-search-private.h>  /* for camel_search_word */
 
 #include <mail/e-mail-folder-create-dialog.h>
diff --git a/modules/mail/em-composer-prefs.c b/modules/mail/em-composer-prefs.c
index 3861519..8ec3f6f 100644
--- a/modules/mail/em-composer-prefs.c
+++ b/modules/mail/em-composer-prefs.c
@@ -34,10 +34,6 @@
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 
-#include <gtkhtml/gtkhtml.h>
-#include <editor/gtkhtml-spell-language.h>
-#include <libedataserver/libedataserver.h>
-
 #include <composer/e-msg-composer.h>
 
 #include <shell/e-shell-utils.h>
@@ -56,53 +52,6 @@ G_DEFINE_TYPE (
        em_composer_prefs,
        GTK_TYPE_VBOX)
 
-static gboolean
-composer_prefs_map_string_to_color (GValue *value,
-                                    GVariant *variant,
-                                    gpointer user_data)
-{
-       GdkColor color;
-       const gchar *string;
-       gboolean success = FALSE;
-
-       string = g_variant_get_string (variant, NULL);
-       if (gdk_color_parse (string, &color)) {
-               g_value_set_boxed (value, &color);
-               success = TRUE;
-       }
-
-       return success;
-}
-
-static GVariant *
-composer_prefs_map_color_to_string (const GValue *value,
-                                    const GVariantType *expected_type,
-                                    gpointer user_data)
-{
-       GVariant *variant;
-       const GdkColor *color;
-
-       color = g_value_get_boxed (value);
-       if (color == NULL) {
-               variant = g_variant_new_string ("");
-       } else {
-               gchar *string;
-
-               /* Encode the color manually because CSS styles expect
-                * color codes as #rrggbb, whereas gdk_color_to_string()
-                * returns color codes as #rrrrggggbbbb. */
-               string = g_strdup_printf (
-                       "#%02x%02x%02x",
-                       (gint) color->red * 256 / 65536,
-                       (gint) color->green * 256 / 65536,
-                       (gint) color->blue * 256 / 65536);
-               variant = g_variant_new_string (string);
-               g_free (string);
-       }
-
-       return variant;
-}
-
 static void
 composer_prefs_dispose (GObject *object)
 {
@@ -168,7 +117,7 @@ spell_language_save (EMComposerPrefs *prefs)
        /* Build a list of active spell languages. */
        valid = gtk_tree_model_get_iter_first (model, &iter);
        while (valid) {
-               const GtkhtmlSpellLanguage *language;
+               ESpellDictionary *language;
                gboolean active;
 
                gtk_tree_model_get (
@@ -191,36 +140,36 @@ spell_language_save (EMComposerPrefs *prefs)
 static void
 spell_setup (EMComposerPrefs *prefs)
 {
-       const GList *available_languages;
-       GList *active_languages;
+       GList *list, *link;
        GtkListStore *store;
 
        store = GTK_LIST_STORE (prefs->language_model);
-       available_languages = gtkhtml_spell_language_get_available ();
 
-       active_languages = e_load_spell_languages ();
+       list = e_spell_checker_list_available_dicts (prefs->spell_checker);
 
        /* Populate the GtkListStore. */
-       while (available_languages != NULL) {
-               const GtkhtmlSpellLanguage *language;
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               ESpellDictionary *dictionary;
                GtkTreeIter tree_iter;
                const gchar *name;
+               const gchar *code;
                gboolean active;
 
-               language = available_languages->data;
-               name = gtkhtml_spell_language_get_name (language);
-               active = (g_list_find (active_languages, language) != NULL);
+               dictionary = E_SPELL_DICTIONARY (link->data);
+               name = e_spell_dictionary_get_name (dictionary);
+               code = e_spell_dictionary_get_code (dictionary);
+
+               active = e_spell_checker_get_language_active (
+                       prefs->spell_checker, code);
 
                gtk_list_store_append (store, &tree_iter);
 
                gtk_list_store_set (
                        store, &tree_iter,
-                       0, active, 1, name, 2, language, -1);
-
-               available_languages = available_languages->next;
+                       0, active, 1, name, 2, dictionary, -1);
        }
 
-       g_list_free (active_languages);
+       g_list_free (list);
 }
 
 #define MAIL_SEND_ACCOUNT_OVERRIDE_KEY "sao-mail-send-account-override"
@@ -1006,10 +955,15 @@ static EMConfigItem emcp_items[] = {
          (gchar *) "vboxSpellChecking",
          emcp_widget_glade },
 
-       { E_CONFIG_PAGE,
-         (gchar *) "90.accountoverride",
-         (gchar *) "send-account-override-grid",
-         emcp_widget_glade }
+       { E_CONFIG_SECTION_TABLE,
+         (gchar *) "20.spellcheck/00.languages",
+         (gchar *) "languages-table",
+         emcp_widget_glade },
+
+       { E_CONFIG_SECTION,
+         (gchar *) "20.spellcheck/00.options",
+         (gchar *) "spell-options-vbox",
+         emcp_widget_glade },
 };
 
 static void
@@ -1051,6 +1005,8 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
        prefs->builder = gtk_builder_new ();
        e_load_ui_builder_definition (prefs->builder, "mail-config.ui");
 
+       prefs->spell_checker = e_spell_checker_new ();
+
        /** @HookPoint-EMConfig: Mail Composer Preferences
         * @Id: org.gnome.evolution.mail.composerPrefs
         * @Class: org.gnome.evolution.mail.config:1.0
@@ -1134,6 +1090,12 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
                widget, "active",
                G_SETTINGS_BIND_DEFAULT);
 
+       widget = e_builder_get_widget (prefs->builder, "spinWordWrapLength");
+       g_settings_bind (
+               settings, "composer-word-wrap-length",
+               widget, "value",
+               G_SETTINGS_BIND_DEFAULT);
+
        widget = e_builder_get_widget (prefs->builder, "chkOutlookFilenames");
        g_settings_bind (
                settings, "composer-outlook-filenames",
@@ -1193,9 +1155,6 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
        view = GTK_TREE_VIEW (widget);
        store = gtk_list_store_new (
                3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
-       g_signal_connect_swapped (
-               store, "row-changed",
-               G_CALLBACK (spell_language_save), prefs);
        prefs->language_model = GTK_TREE_MODEL (store);
        gtk_tree_view_set_model (view, prefs->language_model);
        renderer = gtk_cell_renderer_toggle_new ();
@@ -1215,19 +1174,14 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
        info_pixmap = e_builder_get_widget (prefs->builder, "pixmapSpellInfo");
        gtk_image_set_from_icon_name (
                GTK_IMAGE (info_pixmap),
-               "dialog-information", GTK_ICON_SIZE_BUTTON);
-
-       widget = e_builder_get_widget (prefs->builder, "colorButtonSpellCheckColor");
-       g_settings_bind_with_mapping (
-               settings, "composer-spell-color",
-               widget, "color",
-               G_SETTINGS_BIND_DEFAULT,
-               composer_prefs_map_string_to_color,
-               composer_prefs_map_color_to_string,
-               NULL, (GDestroyNotify) NULL);
+               GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON);
 
        spell_setup (prefs);
 
+       g_signal_connect_swapped (
+               store, "row-changed",
+               G_CALLBACK (spell_language_save), prefs);
+
        /* Forwards and Replies */
        widget = e_builder_get_widget (prefs->builder, "comboboxForwardStyle");
        g_settings_bind (
diff --git a/modules/mail/em-composer-prefs.h b/modules/mail/em-composer-prefs.h
index cb986f5..5b72293 100644
--- a/modules/mail/em-composer-prefs.h
+++ b/modules/mail/em-composer-prefs.h
@@ -23,7 +23,6 @@
 #define EM_COMPOSER_PREFS_H
 
 #include <gtk/gtk.h>
-#include <gtkhtml/gtkhtml.h>
 
 #include <shell/e-shell.h>
 
@@ -67,7 +66,9 @@ struct _EMComposerPrefs {
        GtkComboBox *reply_style;
 
        /* Signatures */
-       GtkHTML *sig_preview;
+       EWebViewPreview *sig_preview;
+
+       ESpellChecker *spell_checker;
 };
 
 struct _EMComposerPrefsClass {
diff --git a/modules/mail/em-mailer-prefs.c b/modules/mail/em-mailer-prefs.c
index 3e3aa39..0e2b11b 100644
--- a/modules/mail/em-mailer-prefs.c
+++ b/modules/mail/em-mailer-prefs.c
@@ -28,7 +28,6 @@
 
 #include "em-mailer-prefs.h"
 
-#include <gtkhtml/gtkhtml-properties.h>
 #include <libxml/tree.h>
 
 #include <shell/e-shell-utils.h>
@@ -738,7 +737,7 @@ image_loading_policy_always_cb (GtkToggleButton *toggle_button)
 
                g_settings_set_enum (
                        settings, "image-loading-policy",
-                       E_MAIL_IMAGE_LOADING_POLICY_ALWAYS);
+                       E_IMAGE_LOADING_POLICY_ALWAYS);
 
                g_object_unref (settings);
        }
@@ -754,7 +753,7 @@ image_loading_policy_sometimes_cb (GtkToggleButton *toggle_button)
 
                g_settings_set_enum (
                        settings, "image-loading-policy",
-                       E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES);
+                       E_IMAGE_LOADING_POLICY_SOMETIMES);
 
                g_object_unref (settings);
        }
@@ -770,7 +769,7 @@ image_loading_policy_never_cb (GtkToggleButton *toggle_button)
 
                g_settings_set_enum (
                        settings, "image-loading-policy",
-                       E_MAIL_IMAGE_LOADING_POLICY_NEVER);
+                       E_IMAGE_LOADING_POLICY_NEVER);
 
                g_object_unref (settings);
        }
@@ -1006,7 +1005,7 @@ em_mailer_prefs_construct (EMMailerPrefs *prefs,
                prefs->builder, "radImagesNever");
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (widget),
-               val == E_MAIL_IMAGE_LOADING_POLICY_NEVER);
+               val == E_IMAGE_LOADING_POLICY_NEVER);
        gtk_widget_set_sensitive (widget, writable);
 
        g_signal_connect (
@@ -1017,7 +1016,7 @@ em_mailer_prefs_construct (EMMailerPrefs *prefs,
                prefs->builder, "radImagesSometimes");
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (widget),
-               val == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES);
+               val == E_IMAGE_LOADING_POLICY_SOMETIMES);
        gtk_widget_set_sensitive (widget, writable);
 
        g_signal_connect (
@@ -1028,7 +1027,7 @@ em_mailer_prefs_construct (EMMailerPrefs *prefs,
                prefs->builder, "radImagesAlways");
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (widget),
-               val == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS);
+               val == E_IMAGE_LOADING_POLICY_ALWAYS);
        gtk_widget_set_sensitive (widget, writable);
 
        g_signal_connect (
diff --git a/modules/mailto-handler/Makefile.am b/modules/mailto-handler/Makefile.am
index a4d90d3..624ca37 100644
--- a/modules/mailto-handler/Makefile.am
+++ b/modules/mailto-handler/Makefile.am
@@ -6,8 +6,8 @@ module_mailto_handler_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-mailto-handler\"             \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_mailto_handler_la_SOURCES = \
        evolution-mailto-handler.c
@@ -17,7 +17,7 @@ module_mailto_handler_la_LIBADD = \
        $(top_builddir)/shell/libevolution-shell.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_mailto_handler_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/mdn/Makefile.am b/modules/mdn/Makefile.am
index 550aa08..ba43b99 100644
--- a/modules/mdn/Makefile.am
+++ b/modules/mdn/Makefile.am
@@ -6,8 +6,8 @@ module_mdn_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-mdn\"                        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_mdn_la_SOURCES = \
        evolution-mdn.c
@@ -19,7 +19,7 @@ module_mdn_la_LIBADD = \
        $(top_builddir)/shell/libevolution-shell.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_mdn_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/offline-alert/Makefile.am b/modules/offline-alert/Makefile.am
index 68ef0d2..8a20803 100644
--- a/modules/offline-alert/Makefile.am
+++ b/modules/offline-alert/Makefile.am
@@ -6,8 +6,8 @@ module_offline_alert_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-offline-alert\"              \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_offline_alert_la_SOURCES = \
        evolution-offline-alert.c
@@ -17,7 +17,7 @@ module_offline_alert_la_LIBADD = \
        $(top_builddir)/shell/libevolution-shell.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_offline_alert_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/plugin-lib/Makefile.am b/modules/plugin-lib/Makefile.am
index 48506c0..d94320a 100644
--- a/modules/plugin-lib/Makefile.am
+++ b/modules/plugin-lib/Makefile.am
@@ -7,8 +7,8 @@ module_plugin_lib_la_CPPFLAGS = \
        -DEVOLUTION_PREFIX=\""$(prefix)"\"                      \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_plugin_lib_la_SOURCES = \
        evolution-module-plugin-lib.c                           \
@@ -19,7 +19,7 @@ module_plugin_lib_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_plugin_lib_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/plugin-manager/Makefile.am b/modules/plugin-manager/Makefile.am
index eef64c2..c00ce41 100644
--- a/modules/plugin-manager/Makefile.am
+++ b/modules/plugin-manager/Makefile.am
@@ -6,8 +6,8 @@ module_plugin_manager_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-plugin-manager\"             \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_plugin_manager_la_SOURCES = \
        evolution-plugin-manager.c
@@ -17,7 +17,7 @@ module_plugin_manager_la_LIBADD = \
        $(top_builddir)/shell/libevolution-shell.la             \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_plugin_manager_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/prefer-plain/Makefile.am b/modules/prefer-plain/Makefile.am
index e3b546b..9b6c8b3 100644
--- a/modules/prefer-plain/Makefile.am
+++ b/modules/prefer-plain/Makefile.am
@@ -9,8 +9,8 @@ module_prefer_plain_la_CPPFLAGS =                               \
        -DG_LOG_DOMAIN=\"evolution-module-prefer-plain\"        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_prefer_plain_la_SOURCES =                               \
        e-mail-parser-prefer-plain.c                            \
@@ -26,7 +26,7 @@ module_prefer_plain_la_LIBADD =                                               \
        $(top_builddir)/shell/libevolution-shell.la                     \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_prefer_plain_la_LDFLAGS =                               \
        -avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/prefer-plain/plugin/Makefile.am b/modules/prefer-plain/plugin/Makefile.am
index b3179af..e1f73c6 100644
--- a/modules/prefer-plain/plugin/Makefile.am
+++ b/modules/prefer-plain/plugin/Makefile.am
@@ -9,8 +9,8 @@ liborg_gnome_prefer_plain_la_CPPFLAGS =         \
        -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\"    \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_prefer_plain_la_SOURCES =         \
        config-ui.c
@@ -20,7 +20,7 @@ liborg_gnome_prefer_plain_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) $(
 liborg_gnome_prefer_plain_la_LIBADD =                          \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 BUILT_SOURCES = $(plugin_DATA)
 
diff --git a/modules/settings/Makefile.am b/modules/settings/Makefile.am
index 3c9bdb0..c03253d 100644
--- a/modules/settings/Makefile.am
+++ b/modules/settings/Makefile.am
@@ -8,7 +8,6 @@ module_settings_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-module-settings\" \
        $(EVOLUTION_DATA_SERVER_CFLAGS) \
        $(GNOME_PLATFORM_CFLAGS) \
-       $(GTKHTML_CFLAGS) \
        $(CODE_COVERAGE_CFLAGS) \
        $(NULL)
 
@@ -46,12 +45,12 @@ module_settings_la_SOURCES = \
        e-settings-message-list.h \
        e-settings-name-selector-entry.c \
        e-settings-name-selector-entry.h \
+       e-settings-spell-checker.c \
+       e-settings-spell-checker.h \
        e-settings-spell-entry.c \
        e-settings-spell-entry.h \
        e-settings-web-view.c \
        e-settings-web-view.h \
-       e-settings-web-view-gtkhtml.c \
-       e-settings-web-view-gtkhtml.h \
        e-settings-weekday-chooser.c \
        e-settings-weekday-chooser.h \
        $(NULL)
@@ -65,7 +64,6 @@ module_settings_la_LIBADD = \
        $(top_builddir)/calendar/gui/libevolution-calendar.la \
        $(EVOLUTION_DATA_SERVER_LIBS) \
        $(GNOME_PLATFORM_LIBS) \
-       $(GTKHTML_LIBS) \
        $(NULL)
 
 module_settings_la_LDFLAGS = \
diff --git a/modules/settings/e-settings-deprecated.c b/modules/settings/e-settings-deprecated.c
index b34e791..d519a91 100644
--- a/modules/settings/e-settings-deprecated.c
+++ b/modules/settings/e-settings-deprecated.c
@@ -332,7 +332,7 @@ static void
 settings_deprecated_image_loading_policy_cb (GSettings *settings,
                                              const gchar *key)
 {
-       EMailImageLoadingPolicy policy;
+       EImageLoadingPolicy policy;
 
        policy = g_settings_get_enum (settings, "image-loading-policy");
        e_settings_deprecated_set_int_with_change_test (settings, "load-http-images", policy);
diff --git a/modules/settings/e-settings-spell-checker.c b/modules/settings/e-settings-spell-checker.c
new file mode 100644
index 0000000..6e5f0cc
--- /dev/null
+++ b/modules/settings/e-settings-spell-checker.c
@@ -0,0 +1,117 @@
+/*
+ * e-settings-spell-checker.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-settings-spell-checker.h"
+
+#include <e-util/e-util.h>
+
+#define E_SETTINGS_SPELL_CHECKER_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_SETTINGS_SPELL_CHECKER, ESettingsSpellCheckerPrivate))
+
+struct _ESettingsSpellCheckerPrivate {
+       gint placeholder;
+};
+
+G_DEFINE_DYNAMIC_TYPE (
+       ESettingsSpellChecker,
+       e_settings_spell_checker,
+       E_TYPE_EXTENSION)
+
+static ESpellChecker *
+settings_spell_checker_get_extensible (ESettingsSpellChecker *extension)
+{
+       EExtensible *extensible;
+
+       extensible = e_extension_get_extensible (E_EXTENSION (extension));
+
+       return E_SPELL_CHECKER (extensible);
+}
+
+static void
+settings_spell_checker_constructed (GObject *object)
+{
+       ESpellChecker *spell_checker;
+       GSettings *settings;
+       gchar **strv;
+       guint ii;
+
+       /* Chain up to parent's constructed() method. */
+       G_OBJECT_CLASS (e_settings_spell_checker_parent_class)->
+               constructed (object);
+
+       /* This only initializes the active spell languages, it does not
+        * write changes back to GSettings.  Only the ESpellChecker used
+        * in Composer Preferences should be doing that. */
+
+       spell_checker = settings_spell_checker_get_extensible (
+               E_SETTINGS_SPELL_CHECKER (object));
+
+       /* Make sure there are no active languages at this point. */
+       g_warn_if_fail (
+               e_spell_checker_count_active_languages (spell_checker) == 0);
+
+       settings = g_settings_new ("org.gnome.evolution.mail");
+       strv = g_settings_get_strv (settings, "composer-spell-languages");
+       g_object_unref (settings);
+
+       g_return_if_fail (strv != NULL);
+
+       for (ii = 0; strv[ii] != NULL; ii++)
+               e_spell_checker_set_language_active (
+                       spell_checker, strv[ii], TRUE);
+
+       g_strfreev (strv);
+}
+
+static void
+e_settings_spell_checker_class_init (ESettingsSpellCheckerClass *class)
+{
+       GObjectClass *object_class;
+       EExtensionClass *extension_class;
+
+       g_type_class_add_private (
+               class, sizeof (ESettingsSpellCheckerPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->constructed = settings_spell_checker_constructed;
+
+       extension_class = E_EXTENSION_CLASS (class);
+       extension_class->extensible_type = E_TYPE_SPELL_CHECKER;
+}
+
+static void
+e_settings_spell_checker_class_finalize (ESettingsSpellCheckerClass *class)
+{
+}
+
+static void
+e_settings_spell_checker_init (ESettingsSpellChecker *extension)
+{
+       extension->priv = E_SETTINGS_SPELL_CHECKER_GET_PRIVATE (extension);
+}
+
+void
+e_settings_spell_checker_type_register (GTypeModule *type_module)
+{
+       /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+        *     function, so we have to wrap it with a public function in
+        *     order to register types from a separate compilation unit. */
+       e_settings_spell_checker_register_type (type_module);
+}
+
diff --git a/modules/settings/e-settings-spell-checker.h b/modules/settings/e-settings-spell-checker.h
new file mode 100644
index 0000000..3e60cec
--- /dev/null
+++ b/modules/settings/e-settings-spell-checker.h
@@ -0,0 +1,65 @@
+/*
+ * e-settings-spell-checker.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_SETTINGS_SPELL_CHECKER_H
+#define E_SETTINGS_SPELL_CHECKER_H
+
+#include <libebackend/libebackend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SETTINGS_SPELL_CHECKER \
+       (e_settings_spell_checker_get_type ())
+#define E_SETTINGS_SPELL_CHECKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SETTINGS_SPELL_CHECKER, ESettingsSpellChecker))
+#define E_SETTINGS_SPELL_CHECKER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SETTINGS_SPELL_CHECKER, ESettingsSpellCheckerClass))
+#define E_IS_SETTINGS_SPELL_CHECKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SETTINGS_SPELL_CHECKER))
+#define E_IS_SETTINGS_SPELL_CHECKER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SETTINGS_SPELL_CHECKER))
+#define E_SETTINGS_SPELL_CHECKER_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SETTINGS_SPELL_CHECKER, ESettingsSpellCheckerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESettingsSpellChecker ESettingsSpellChecker;
+typedef struct _ESettingsSpellCheckerClass ESettingsSpellCheckerClass;
+typedef struct _ESettingsSpellCheckerPrivate ESettingsSpellCheckerPrivate;
+
+struct _ESettingsSpellChecker {
+       EExtension parent;
+       ESettingsSpellCheckerPrivate *priv;
+};
+
+struct _ESettingsSpellCheckerClass {
+       EExtensionClass parent_class;
+};
+
+GType          e_settings_spell_checker_get_type
+                                               (void) G_GNUC_CONST;
+void           e_settings_spell_checker_type_register
+                                               (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_SETTINGS_SPELL_CHECKER_H */
diff --git a/modules/settings/e-settings-web-view.c b/modules/settings/e-settings-web-view.c
index 0867b3c..ff804f3 100644
--- a/modules/settings/e-settings-web-view.c
+++ b/modules/settings/e-settings-web-view.c
@@ -31,7 +31,8 @@
        ((obj), E_TYPE_SETTINGS_WEB_VIEW, ESettingsWebViewPrivate))
 
 struct _ESettingsWebViewPrivate {
-       gint placeholder;
+       GtkCssProvider *css_provider;
+       GSettings *settings;
 };
 
 G_DEFINE_DYNAMIC_TYPE (
@@ -39,32 +40,194 @@ G_DEFINE_DYNAMIC_TYPE (
        e_settings_web_view,
        E_TYPE_EXTENSION)
 
+/* replaces content of color string */
 static void
-settings_web_view_constructed (GObject *object)
+settings_web_view_fix_color_string (gchar *color_string)
 {
-       GSettings *settings;
+       GdkColor color;
+
+       if (color_string == NULL)
+               return;
+
+       if (strlen (color_string) < 13)
+               return;
+
+       if (!gdk_color_parse (color_string, &color))
+               return;
+
+       sprintf (
+               color_string, "#%02x%02x%02x",
+               (gint) color.red * 256 / 65536,
+               (gint) color.green * 256 / 65536,
+               (gint) color.blue * 256 / 65536);
+}
+
+static void
+settings_web_view_load_style (ESettingsWebView *extension)
+{
+       GString *buffer;
+       gchar *citation_color;
+       gchar *monospace_font;
+       gchar *variable_font;
+       gboolean custom_fonts;
+       gboolean mark_citations;
        EExtensible *extensible;
+       GtkStyleContext *style_context;
+       GSettings *settings;
+       GError *error = NULL;
 
-       extensible = e_extension_get_extensible (E_EXTENSION (object));
+       /* Some of our mail and composer preferences are passed down to
+        * GtkHtml through style properties, unfortunately.  This builds
+        * a style sheet for the EWebView using values from GSettings. */
 
-       settings = g_settings_new ("org.gnome.evolution.mail");
+       settings = extension->priv->settings;
+
+       custom_fonts =
+               g_settings_get_boolean (settings, "use-custom-font");
+       monospace_font =
+               g_settings_get_string (settings, "monospace-font");
+       variable_font =
+               g_settings_get_string (settings, "variable-width-font");
+       mark_citations =
+               g_settings_get_boolean (settings, "mark-citations");
+       citation_color =
+               g_settings_get_string (settings, "citation-color");
+
+       buffer = g_string_new ("EWebViewGtkHTML {\n");
+
+       settings_web_view_fix_color_string (citation_color);
+
+       if (custom_fonts && variable_font != NULL)
+               g_string_append_printf (
+                       buffer, "  font: %s;\n", variable_font);
+
+       if (custom_fonts && monospace_font != NULL)
+               g_string_append_printf (
+                       buffer, "  -GtkHTML-fixed-font-name: '%s';\n",
+                       monospace_font);
+
+       if (mark_citations && citation_color != NULL)
+               g_string_append_printf (
+                       buffer, "  -GtkHTML-cite-color: %s;\n",
+                       citation_color);
+
+       g_string_append (buffer, "}\n");
+
+       gtk_css_provider_load_from_data (
+               extension->priv->css_provider,
+               buffer->str, buffer->len, &error);
+
+       if (error != NULL) {
+               g_warning ("%s", error->message);
+               g_error_free (error);
+       }
+
+       g_string_free (buffer, TRUE);
+
+       g_free (monospace_font);
+       g_free (variable_font);
+       g_free (citation_color);
+
+       extensible = e_extension_get_extensible (E_EXTENSION (extension));
+       style_context = gtk_widget_get_style_context (GTK_WIDGET (extensible));
+       gtk_style_context_invalidate (style_context);
+}
+
+static void
+settings_web_view_changed_cb (GSettings *settings,
+                              const gchar *key,
+                              ESettingsWebView *extension)
+{
+       settings_web_view_load_style (extension);
+}
+
+static void
+settings_web_view_realize (GtkWidget *widget,
+                           ESettingsWebView *extension)
+{
+       GSettings *settings;
+
+       settings = extension->priv->settings;
 
        g_settings_bind (
                settings, "composer-inline-spelling",
-               extensible, "inline-spelling",
+               widget, "inline-spelling",
                G_SETTINGS_BIND_GET);
 
        g_settings_bind (
                settings, "composer-magic-links",
-               extensible, "magic-links",
+               widget, "magic-links",
                G_SETTINGS_BIND_GET);
 
        g_settings_bind (
                settings, "composer-magic-smileys",
-               extensible, "magic-smileys",
+               widget, "magic-smileys",
                G_SETTINGS_BIND_GET);
 
-       g_object_unref (settings);
+       gtk_style_context_add_provider (
+               gtk_widget_get_style_context (widget),
+               GTK_STYLE_PROVIDER (extension->priv->css_provider),
+               GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+       settings_web_view_load_style (extension);
+
+       /* Reload the style sheet when certain settings change. */
+
+       g_signal_connect (
+               settings, "changed::use-custom-font",
+               G_CALLBACK (settings_web_view_changed_cb), extension);
+
+       g_signal_connect (
+               settings, "changed::monospace-font",
+               G_CALLBACK (settings_web_view_changed_cb), extension);
+
+       g_signal_connect (
+               settings, "changed::variable-width-font",
+               G_CALLBACK (settings_web_view_changed_cb), extension);
+
+       g_signal_connect (
+               settings, "changed::mark-citations",
+               G_CALLBACK (settings_web_view_changed_cb), extension);
+
+       g_signal_connect (
+               settings, "changed::citation-color",
+               G_CALLBACK (settings_web_view_changed_cb), extension);
+}
+
+static void
+settings_web_view_dispose (GObject *object)
+{
+       ESettingsWebViewPrivate *priv;
+
+       priv = E_SETTINGS_WEB_VIEW_GET_PRIVATE (object);
+
+       if (priv->settings != NULL) {
+               g_signal_handlers_disconnect_by_func (
+                       priv->settings,
+                       settings_web_view_changed_cb, object);
+       }
+
+       g_clear_object (&priv->css_provider);
+       g_clear_object (&priv->settings);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_settings_web_view_parent_class)->dispose (object);
+}
+
+static void
+settings_web_view_constructed (GObject *object)
+{
+       EExtensible *extensible;
+
+       extensible = e_extension_get_extensible (E_EXTENSION (object));
+
+       /* Wait to bind settings until the EWebView is realized so
+        * GtkhtmlEditor has a chance to install a GtkHTMLEditorAPI.
+        * Otherwise our settings will have no effect. */
+
+       g_signal_connect (
+               extensible, "realize",
+               G_CALLBACK (settings_web_view_realize), object);
 
        /* Chain up to parent's constructed() method. */
        G_OBJECT_CLASS (e_settings_web_view_parent_class)->
@@ -80,6 +243,7 @@ e_settings_web_view_class_init (ESettingsWebViewClass *class)
        g_type_class_add_private (class, sizeof (ESettingsWebViewPrivate));
 
        object_class = G_OBJECT_CLASS (class);
+       object_class->dispose = settings_web_view_dispose;
        object_class->constructed = settings_web_view_constructed;
 
        extension_class = E_EXTENSION_CLASS (class);
@@ -94,7 +258,14 @@ e_settings_web_view_class_finalize (ESettingsWebViewClass *class)
 static void
 e_settings_web_view_init (ESettingsWebView *extension)
 {
+       GSettings *settings;
+
        extension->priv = E_SETTINGS_WEB_VIEW_GET_PRIVATE (extension);
+
+       extension->priv->css_provider = gtk_css_provider_new ();
+
+       settings = g_settings_new ("org.gnome.evolution.mail");
+       extension->priv->settings = settings;
 }
 
 void
diff --git a/modules/settings/evolution-module-settings.c b/modules/settings/evolution-module-settings.c
index af08158..88101b5 100644
--- a/modules/settings/evolution-module-settings.c
+++ b/modules/settings/evolution-module-settings.c
@@ -31,9 +31,9 @@
 #include "e-settings-meeting-time-selector.h"
 #include "e-settings-message-list.h"
 #include "e-settings-name-selector-entry.h"
+#include "e-settings-spell-checker.h"
 #include "e-settings-spell-entry.h"
 #include "e-settings-web-view.h"
-#include "e-settings-web-view-gtkhtml.h"
 #include "e-settings-weekday-chooser.h"
 
 /* Module Entry Points */
@@ -59,9 +59,9 @@ e_module_load (GTypeModule *type_module)
        e_settings_meeting_time_selector_type_register (type_module);
        e_settings_message_list_type_register (type_module);
        e_settings_name_selector_entry_type_register (type_module);
+       e_settings_spell_checker_type_register (type_module);
        e_settings_spell_entry_type_register (type_module);
        e_settings_web_view_type_register (type_module);
-       e_settings_web_view_gtkhtml_type_register (type_module);
        e_settings_weekday_chooser_type_register (type_module);
 }
 
diff --git a/modules/spamassassin/Makefile.am b/modules/spamassassin/Makefile.am
index 2ecb217..ae5c32d 100644
--- a/modules/spamassassin/Makefile.am
+++ b/modules/spamassassin/Makefile.am
@@ -6,8 +6,8 @@ module_spamassassin_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-spamassassin\"               \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 module_spamassassin_la_SOURCES = \
        evolution-spamassassin.c
@@ -19,7 +19,7 @@ module_spamassassin_la_LIBADD = \
        $(top_builddir)/libemail-engine/libemail-engine.la \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_spamassassin_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/startup-wizard/Makefile.am b/modules/startup-wizard/Makefile.am
index 68b08f6..4bf9971 100644
--- a/modules/startup-wizard/Makefile.am
+++ b/modules/startup-wizard/Makefile.am
@@ -8,7 +8,6 @@ module_startup_wizard_la_CPPFLAGS = \
        -DG_LOG_DOMAIN=\"evolution-startup-wizard\"             \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(CODE_COVERAGE_CFLAGS)                                 \
        $(NULL)
 
@@ -31,7 +30,6 @@ module_startup_wizard_la_LIBADD = \
        $(libevolution_mail_settings_la)                        \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
        $(NULL)
 
 module_startup_wizard_la_LDFLAGS = \
diff --git a/modules/text-highlight/Makefile.am b/modules/text-highlight/Makefile.am
index f105ff7..ab5df67 100644
--- a/modules/text-highlight/Makefile.am
+++ b/modules/text-highlight/Makefile.am
@@ -7,8 +7,8 @@ module_text_highlight_la_CPPFLAGS =                                     \
        -DG_LOG_DOMAIN=\"evolution-module-text-highlight\"              \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                                 \
        $(GNOME_PLATFORM_CFLAGS)                                        \
-       $(GTKHTML_CFLAGS)                                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                         \
+       $(NULL)
 
 module_text_highlight_la_SOURCES =                                     \
        e-mail-display-popup-text-highlight.c                           \
@@ -28,7 +28,7 @@ module_text_highlight_la_LIBADD =                                     \
        $(top_builddir)/shell/libevolution-shell.la                     \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_text_highlight_la_LDFLAGS =                                     \
        -avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/tnef-attachment/Makefile.am b/modules/tnef-attachment/Makefile.am
index 09ff602..4ba29db 100644
--- a/modules/tnef-attachment/Makefile.am
+++ b/modules/tnef-attachment/Makefile.am
@@ -13,9 +13,9 @@ module_tnef_attachment_la_CPPFLAGS =                  \
        -DG_LOG_DOMAIN=\"evolution-module-tnef-attachment\"             \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                                 \
        $(GNOME_PLATFORM_CFLAGS)                                        \
-       $(GTKHTML_CFLAGS)                                               \
        $(TNEF_CFLAGS)                                                  \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                         \
+       $(NULL)
 
 module_tnef_attachment_la_SOURCES =                    \
        e-mail-parser-tnef-attachment.c                                 \
@@ -27,8 +27,8 @@ module_tnef_attachment_la_LIBADD =                            \
        $(top_builddir)/em-format/libevolution-mail-formatter.la        \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)                                                 \
-       -lytnef
+       -lytnef                                                         \
+       $(NULL)
 
 module_tnef_attachment_la_LDFLAGS =                    \
        -avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/vcard-inline/Makefile.am b/modules/vcard-inline/Makefile.am
index 22af723..5f5a6e5 100644
--- a/modules/vcard-inline/Makefile.am
+++ b/modules/vcard-inline/Makefile.am
@@ -7,8 +7,8 @@ module_vcard_inline_la_CPPFLAGS =                               \
        -DG_LOG_DOMAIN=\"evolution-module-vcard-inline\"        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(CODE_COVERAGE_CFLAGS)
+       $(NULL)
 
 module_vcard_inline_la_SOURCES =                               \
        e-mail-formatter-vcard.c                                \
@@ -27,7 +27,7 @@ module_vcard_inline_la_LIBADD =                                               \
        $(top_builddir)/addressbook/printing/libecontactprint.la        \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_vcard_inline_la_LDFLAGS =                               \
        -avoid-version -module $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/modules/web-inspector/Makefile.am b/modules/web-inspector/Makefile.am
index 43615d5..237121e 100644
--- a/modules/web-inspector/Makefile.am
+++ b/modules/web-inspector/Makefile.am
@@ -6,8 +6,8 @@ module_web_inspector_la_CPPFLAGS =                      \
        -DG_LOG_DOMAIN=\"evolution-web-inspector\"      \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 module_web_inspector_la_SOURCES = \
        evolution-web-inspector.c
@@ -16,7 +16,7 @@ module_web_inspector_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la     \
        $(EVOLUTION_DATA_SERVER_LIBS)                   \
        $(GNOME_PLATFORM_LIBS)                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 module_web_inspector_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/plugins/attachment-reminder/Makefile.am b/plugins/attachment-reminder/Makefile.am
index 9fbbdb1..023dab3 100644
--- a/plugins/attachment-reminder/Makefile.am
+++ b/plugins/attachment-reminder/Makefile.am
@@ -16,8 +16,8 @@ liborg_gnome_evolution_attachment_reminder_la_CPPFLAGS = \
        -DEVOLUTION_PLUGINDIR="\"$(plugindir)\""        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_evolution_attachment_reminder_la_SOURCES = attachment-reminder.c 
 
@@ -32,7 +32,7 @@ liborg_gnome_evolution_attachment_reminder_la_LIBADD =        \
        $(top_builddir)/mail/libevolution-mail.la       \
        $(EVOLUTION_DATA_SERVER_LIBS)                   \
        $(GNOME_PLATFORM_LIBS)                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 EXTRA_DIST = org-gnome-evolution-attachment-reminder.eplug.xml \
        org-gnome-attachment-reminder.error.xml
diff --git a/plugins/bbdb/Makefile.am b/plugins/bbdb/Makefile.am
index 991f662..48d8796 100644
--- a/plugins/bbdb/Makefile.am
+++ b/plugins/bbdb/Makefile.am
@@ -17,8 +17,8 @@ liborg_gnome_evolution_bbdb_la_CPPFLAGS =             \
        -I$(top_srcdir)                                 \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_evolution_bbdb_la_SOURCES = bbdb.c bbdb.h gaimbuddies.c
 
@@ -32,7 +32,7 @@ liborg_gnome_evolution_bbdb_la_LIBADD =               \
        $(top_builddir)/addressbook/gui/contact-list-editor/libecontactlisteditor.la \
        $(EVOLUTION_DATA_SERVER_LIBS)           \
        $(GNOME_PLATFORM_LIBS)                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 EXTRA_DIST = org-gnome-evolution-bbdb.eplug.xml
 
diff --git a/plugins/dbx-import/Makefile.am b/plugins/dbx-import/Makefile.am
index 80c5f9f..736faf6 100644
--- a/plugins/dbx-import/Makefile.am
+++ b/plugins/dbx-import/Makefile.am
@@ -16,8 +16,8 @@ liborg_gnome_dbx_import_la_CPPFLAGS =                 \
        -I$(top_builddir)                               \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_dbx_import_la_SOURCES = dbx-importer.c
 
@@ -30,7 +30,7 @@ liborg_gnome_dbx_import_la_LIBADD =                   \
        $(top_builddir)/libemail-engine/libemail-engine.la \
        $(EVOLUTION_DATA_SERVER_LIBS)                   \
        $(GNOME_PLATFORM_LIBS)                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 EXTRA_DIST = org-gnome-dbx-import.eplug.xml
 
diff --git a/plugins/email-custom-header/Makefile.am b/plugins/email-custom-header/Makefile.am
index 7014929..04d0536 100644
--- a/plugins/email-custom-header/Makefile.am
+++ b/plugins/email-custom-header/Makefile.am
@@ -12,8 +12,8 @@ liborg_gnome_email_custom_header_la_CPPFLAGS = \
        -I$(top_builddir)/composer                      \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_email_custom_header_la_SOURCES =          \
        email-custom-header.c                           \
@@ -25,7 +25,7 @@ liborg_gnome_email_custom_header_la_LIBADD =                  \
        $(top_builddir)/mail/libevolution-mail.la               \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 liborg_gnome_email_custom_header_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
 
diff --git a/plugins/email-custom-header/email-custom-header.c 
b/plugins/email-custom-header/email-custom-header.c
index d933e1a..eed2954 100644
--- a/plugins/email-custom-header/email-custom-header.c
+++ b/plugins/email-custom-header/email-custom-header.c
@@ -495,15 +495,16 @@ destroy_compo_data (gpointer data)
 static void
 action_email_custom_header_cb (GtkAction *action,
                                EMsgComposer *composer)
-
 {
        GtkUIManager *ui_manager;
        GtkWidget *menuitem;
        GdkWindow *window;
        CustomHeaderOptionsDialog *dialog = NULL;
        EmailCustomHeaderWindow *new_email_custom_header_window = NULL;
+       EHTMLEditor *editor;
 
-       ui_manager = gtkhtml_editor_get_ui_manager (GTKHTML_EDITOR (composer));
+       editor = e_msg_composer_get_editor (composer);
+       ui_manager = e_html_editor_get_ui_manager (editor);
        menuitem = gtk_ui_manager_get_widget (ui_manager, "/main-menu/insert-menu/insert-menu-top/Custom 
Header");
 
        new_email_custom_header_window = g_object_get_data ((GObject *) composer, "compowindow");
@@ -545,13 +546,13 @@ gboolean
 e_plugin_ui_init (GtkUIManager *ui_manager,
                   EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
 
-       editor = GTKHTML_EDITOR (composer);
+       editor = e_msg_composer_get_editor (composer);
 
        /* Add actions to the "composer" action group. */
        gtk_action_group_add_actions (
-               gtkhtml_editor_get_action_group (editor, "composer"),
+               e_html_editor_get_action_group (editor, "composer"),
                entries, G_N_ELEMENTS (entries), composer);
 
        return TRUE;
diff --git a/plugins/external-editor/Makefile.am b/plugins/external-editor/Makefile.am
index 98c547e..8d325be 100644
--- a/plugins/external-editor/Makefile.am
+++ b/plugins/external-editor/Makefile.am
@@ -28,8 +28,8 @@ liborg_gnome_external_editor_la_CPPFLAGS =            \
        -I$(top_srcdir)/composer                        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_external_editor_la_SOURCES =              \
        external-editor.c
@@ -46,7 +46,7 @@ liborg_gnome_external_editor_la_LIBADD =                      \
        $(top_builddir)/mail/libevolution-mail.la               \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 EXTRA_DIST =                                   \
        org-gnome-external-editor.eplug.xml     \
diff --git a/plugins/external-editor/external-editor.c b/plugins/external-editor/external-editor.c
index 584c0b6..2eb00b6 100644
--- a/plugins/external-editor/external-editor.c
+++ b/plugins/external-editor/external-editor.c
@@ -149,29 +149,28 @@ static void
 enable_disable_composer (EMsgComposer *composer,
                          gboolean enable)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        GtkAction *action;
        GtkActionGroup *action_group;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
-       editor = GTKHTML_EDITOR (composer);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
 
-       if (enable)
-               gtkhtml_editor_run_command (editor, "editable-on");
-       else
-               gtkhtml_editor_run_command (editor, "editable-off");
+       webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), enable);
 
-       action = GTKHTML_EDITOR_ACTION_EDIT_MENU (composer);
+       action = E_HTML_EDITOR_ACTION_EDIT_MENU (editor);
        gtk_action_set_sensitive (action, enable);
 
-       action = GTKHTML_EDITOR_ACTION_FORMAT_MENU (composer);
+       action = E_HTML_EDITOR_ACTION_FORMAT_MENU (editor);
        gtk_action_set_sensitive (action, enable);
 
-       action = GTKHTML_EDITOR_ACTION_INSERT_MENU (composer);
+       action = E_HTML_EDITOR_ACTION_INSERT_MENU (editor);
        gtk_action_set_sensitive (action, enable);
 
-       action_group = gtkhtml_editor_get_action_group (editor, "composer");
+       action_group = e_html_editor_get_action_group (editor, "composer");
        gtk_action_group_set_sensitive (action_group, enable);
 }
 
@@ -192,16 +191,21 @@ static gboolean
 update_composer_text (GArray *array)
 {
        EMsgComposer *composer;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
        gchar *text;
 
        composer = g_array_index (array, gpointer, 0);
        text = g_array_index (array, gpointer, 1);
 
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
+
        e_msg_composer_set_body_text (composer, text, FALSE);
 
        enable_composer (composer);
 
-       gtkhtml_editor_set_changed (GTKHTML_EDITOR (composer), TRUE);
+       e_html_editor_view_set_changed (view, TRUE);
 
        g_free (text);
 
@@ -250,6 +254,49 @@ numlines (const gchar *text,
        return lineno;
 }
 
+static gint
+get_caret_position (EHTMLEditorView *view)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *selection;
+       WebKitDOMRange *range;
+       gint range_count;
+       WebKitDOMNodeList *nodes;
+       gulong ii, length;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       window = webkit_dom_document_get_default_view (document);
+       selection = webkit_dom_dom_window_get_selection (window);
+
+       if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+               return 0;
+
+       range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+       range_count = 0;
+       nodes = webkit_dom_node_get_child_nodes (
+               webkit_dom_node_get_parent_node (
+                       webkit_dom_dom_selection_get_anchor_node (
+                               selection)));
+       length = webkit_dom_node_list_get_length (nodes);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (nodes, ii);
+               if (webkit_dom_node_is_same_node (
+                       node, webkit_dom_dom_selection_get_anchor_node (selection))) {
+
+                       break;
+               } else if (webkit_dom_node_get_node_type (node) == 3) {
+                       gchar *text = webkit_dom_node_get_text_content (node);
+                       range_count += strlen (text);
+                       g_free (text);
+               }
+       }
+
+       return webkit_dom_range_get_start_offset (range, NULL) + range_count;
+}
+
 static gboolean external_editor_running = FALSE;
 static GMutex external_editor_running_lock;
 
@@ -262,18 +309,21 @@ external_editor_thread (gpointer user_data)
        GSettings *settings;
        gchar *editor_cmd_line = NULL, *editor_cmd = NULL, *content;
        gint fd, position = -1, offset = -1;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
 
        /* prefix temp files with evo so .*vimrc can be setup to recognize them */
        fd = g_file_open_tmp ("evoXXXXXX", &filename, NULL);
        if (fd > 0) {
-               gsize length = 0;
-
                close (fd);
                d (printf ("\n\aTemporary-file Name is : [%s] \n\a", filename));
 
                /* Push the text (if there is one) from the composer to the file */
-               content = gtkhtml_editor_get_text_plain (GTKHTML_EDITOR (composer), &length);
-               g_file_set_contents (filename, content, length, NULL);
+               content = e_html_editor_view_get_text_plain (view);
+               g_file_set_contents (filename, content, strlen (content), NULL);
        } else {
                struct run_error_dialog_data *data;
 
@@ -292,7 +342,7 @@ external_editor_thread (gpointer user_data)
        settings = g_settings_new ("org.gnome.evolution.plugin.external-editor");
        editor_cmd = g_settings_get_string (settings, "command");
        if (!editor_cmd) {
-               if (!(editor_cmd = g_strdup (g_getenv ("EDITOR"))))
+               if (!(editor_cmd = g_strdup (g_getenv ("EDITOR"))) )
                        /* Make gedit the default external editor,
                         * if the default schemas are not installed
                         * and no $EDITOR is set. */
@@ -300,11 +350,8 @@ external_editor_thread (gpointer user_data)
        }
        g_object_unref (settings);
 
-       if (g_strrstr (editor_cmd, "vim") != NULL
-           && gtk_html_get_cursor_pos (
-                       gtkhtml_editor_get_html (
-                       GTKHTML_EDITOR (composer)), &position, &offset)
-                               && position >= 0 && offset >= 0) {
+       if (g_strrstr (editor_cmd, "vim") != NULL &&
+           ((position = get_caret_position (view)) > 0)) {
                gchar *tmp = editor_cmd;
                gint lineno;
                gboolean set_nofork;
@@ -385,7 +432,7 @@ external_editor_thread (gpointer user_data)
                }
        }
 
- finished:
+finished:
        g_mutex_lock (&external_editor_running_lock);
        external_editor_running = FALSE;
        g_mutex_unlock (&external_editor_running_lock);
@@ -484,24 +531,23 @@ gboolean
 e_plugin_ui_init (GtkUIManager *manager,
                   EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
-       EWebViewGtkHTML *web_view;
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
 
-       editor = GTKHTML_EDITOR (composer);
+       editor = e_msg_composer_get_editor (composer);
+       view = e_html_editor_get_view (editor);
 
        /* Add actions to the "composer" action group. */
        gtk_action_group_add_actions (
-               gtkhtml_editor_get_action_group (editor, "composer"),
+               e_html_editor_get_action_group (editor, "composer"),
                entries, G_N_ELEMENTS (entries), composer);
 
-       web_view = e_msg_composer_get_web_view (composer);
-
        g_signal_connect (
-               web_view, "key_press_event",
+               view, "key_press_event",
                G_CALLBACK (key_press_cb), composer);
 
        g_signal_connect (
-               web_view, "delete-event",
+               view, "delete-event",
                G_CALLBACK (delete_cb), composer);
 
        return TRUE;
diff --git a/plugins/face/Makefile.am b/plugins/face/Makefile.am
index dd44937..1fd5909 100644
--- a/plugins/face/Makefile.am
+++ b/plugins/face/Makefile.am
@@ -12,8 +12,8 @@ liborg_gnome_face_la_CPPFLAGS =                               \
        -I$(top_builddir)/composer                      \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_face_la_SOURCES = face.c
 
@@ -23,7 +23,7 @@ liborg_gnome_face_la_LIBADD =                                         \
        $(top_builddir)/mail/libevolution-mail.la               \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 liborg_gnome_face_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
 
diff --git a/plugins/face/face.c b/plugins/face/face.c
index e40bdfc..cfcdb96 100644
--- a/plugins/face/face.c
+++ b/plugins/face/face.c
@@ -423,7 +423,7 @@ gboolean
 e_plugin_ui_init (GtkUIManager *ui_manager,
                   EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
 
        static GtkToggleActionEntry entries[] = {
                { "face-plugin",
@@ -444,11 +444,11 @@ e_plugin_ui_init (GtkUIManager *ui_manager,
                g_free (face);
        }
 
-       editor = GTKHTML_EDITOR (composer);
+       editor = e_msg_composer_get_editor (composer);
 
        /* Add actions to the "composer" action group. */
        gtk_action_group_add_toggle_actions (
-               gtkhtml_editor_get_action_group (editor, "composer"),
+               e_html_editor_get_action_group (editor, "composer"),
                entries, G_N_ELEMENTS (entries), composer);
 
        return TRUE;
@@ -464,11 +464,11 @@ void
 face_handle_send (EPlugin *ep,
                   EMEventTargetComposer *target)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
        GtkAction *action;
 
-       editor = GTKHTML_EDITOR (target->composer);
-       action = gtkhtml_editor_get_action (editor, "face-plugin");
+       editor = e_msg_composer_get_editor (target->composer);
+       action = e_html_editor_get_action (editor, "face-plugin");
 
        g_return_if_fail (action != NULL);
 
diff --git a/plugins/mail-notification/Makefile.am b/plugins/mail-notification/Makefile.am
index 521690c..cef5c9a 100644
--- a/plugins/mail-notification/Makefile.am
+++ b/plugins/mail-notification/Makefile.am
@@ -18,8 +18,8 @@ liborg_gnome_mail_notification_la_CPPFLAGS =  \
        $(GNOME_PLATFORM_CFLAGS)                \
        $(LIBNOTIFY_CFLAGS)                     \
        $(CANBERRA_CFLAGS)                      \
-       $(GTKHTML_CFLAGS)                       \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                 \
+       $(NULL)
 
 liborg_gnome_mail_notification_la_SOURCES = mail-notification.c
 
@@ -34,7 +34,7 @@ liborg_gnome_mail_notification_la_LIBADD =                    \
        $(GNOME_PLATFORM_LIBS)                                  \
        $(LIBNOTIFY_LIBS)                                       \
        $(CANBERRA_LIBS)                                        \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 BUILT_SOURCES = $(plugin_DATA)
 
diff --git a/plugins/mail-to-task/Makefile.am b/plugins/mail-to-task/Makefile.am
index fd4c4c5..1d2d925 100644
--- a/plugins/mail-to-task/Makefile.am
+++ b/plugins/mail-to-task/Makefile.am
@@ -9,8 +9,8 @@ liborg_gnome_mail_to_task_la_CPPFLAGS =                 \
        -I$(top_srcdir)                                 \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_mail_to_task_la_SOURCES = mail-to-task.c
 
@@ -25,7 +25,7 @@ liborg_gnome_mail_to_task_la_LIBADD = \
        $(top_builddir)/libemail-engine/libemail-engine.la              \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
        $(GNOME_PLATFORM_LIBS)                                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 EXTRA_DIST = org-gnome-mail-to-task.eplug.xml
 
diff --git a/plugins/mailing-list-actions/Makefile.am b/plugins/mailing-list-actions/Makefile.am
index fcb9f55..cc6e28c 100644
--- a/plugins/mailing-list-actions/Makefile.am
+++ b/plugins/mailing-list-actions/Makefile.am
@@ -9,8 +9,8 @@ liborg_gnome_mailing_list_actions_la_CPPFLAGS =         \
        -I$(top_builddir)/composer                      \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_mailing_list_actions_la_SOURCES = mailing-list-actions.c
 
@@ -24,7 +24,7 @@ liborg_gnome_mailing_list_actions_la_LIBADD =         \
        $(top_builddir)/libemail-engine/libemail-engine.la \
        $(EVOLUTION_DATA_SERVER_LIBS)                   \
        $(GNOME_PLATFORM_LIBS)                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 error_DATA = org-gnome-mailing-list-actions.error
 errordir = $(privdatadir)/errors
diff --git a/plugins/pst-import/Makefile.am b/plugins/pst-import/Makefile.am
index 6e6d89a..755edf8 100644
--- a/plugins/pst-import/Makefile.am
+++ b/plugins/pst-import/Makefile.am
@@ -17,9 +17,9 @@ liborg_gnome_pst_import_la_CPPFLAGS =                 \
        -I$(top_builddir)                               \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
        $(LIBPST_CFLAGS)                                \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_pst_import_la_SOURCES = pst-importer.c
 
@@ -32,8 +32,8 @@ liborg_gnome_pst_import_la_LIBADD =                           \
        $(top_builddir)/libemail-engine/libemail-engine.la      \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
-       $(LIBPST_LIBS)
+       $(LIBPST_LIBS)                                          \
+       $(NULL)
 
 EXTRA_DIST = org-gnome-pst-import.eplug.xml
 
diff --git a/plugins/publish-calendar/Makefile.am b/plugins/publish-calendar/Makefile.am
index a24b979..a34dbd7 100644
--- a/plugins/publish-calendar/Makefile.am
+++ b/plugins/publish-calendar/Makefile.am
@@ -16,9 +16,9 @@ liborg_gnome_publish_calendar_la_CPPFLAGS =           \
        -DEVOLUTION_UIDIR=\""$(uidir)"\"                \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
        $(LIBNOTIFY_CFLAGS)                             \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_publish_calendar_la_SOURCES =     \
        publish-calendar.c                      \
@@ -39,8 +39,8 @@ liborg_gnome_publish_calendar_la_LIBADD =                     \
        $(top_builddir)/calendar/gui/libevolution-calendar.la   \
        $(EVOLUTION_DATA_SERVER_LIBS)                           \
        $(GNOME_PLATFORM_LIBS)                                  \
-       $(GTKHTML_LIBS)                                         \
-       $(LIBNOTIFY_LIBS)
+       $(LIBNOTIFY_LIBS)                                       \
+       $(NULL)
 
 EXTRA_DIST =                                   \
        org-gnome-publish-calendar.eplug.xml    \
diff --git a/plugins/save-calendar/Makefile.am b/plugins/save-calendar/Makefile.am
index 2a70531..eb3bd54 100644
--- a/plugins/save-calendar/Makefile.am
+++ b/plugins/save-calendar/Makefile.am
@@ -9,8 +9,8 @@ liborg_gnome_save_calendar_la_CPPFLAGS =                \
        -I$(top_srcdir)                                 \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_save_calendar_la_SOURCES = \
        save-calendar.c                 \
@@ -26,7 +26,7 @@ liborg_gnome_save_calendar_la_LIBADD =        \
        $(top_builddir)/shell/libevolution-shell.la \
        $(EVOLUTION_DATA_SERVER_LIBS)           \
        $(GNOME_PLATFORM_LIBS)                  \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 EXTRA_DIST = org-gnome-save-calendar.eplug.xml
 
diff --git a/plugins/templates/Makefile.am b/plugins/templates/Makefile.am
index a5d56c7..18d25ea 100644
--- a/plugins/templates/Makefile.am
+++ b/plugins/templates/Makefile.am
@@ -11,8 +11,8 @@ liborg_gnome_templates_la_CPPFLAGS =                  \
        -I$(top_builddir)/composer                      \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 liborg_gnome_templates_la_SOURCES = templates.c
 
@@ -26,7 +26,7 @@ liborg_gnome_templates_la_LIBADD =    \
        $(top_builddir)/libemail-engine/libemail-engine.la \
        $(EVOLUTION_DATA_SERVER_LIBS)                   \
        $(GNOME_PLATFORM_LIBS)                          \
-       $(GTKHTML_LIBS)
+       $(NULL)
 
 EXTRA_DIST =   org-gnome-templates.eplug.xml
 
diff --git a/plugins/templates/templates.c b/plugins/templates/templates.c
index 0a50178..e3883a1 100644
--- a/plugins/templates/templates.c
+++ b/plugins/templates/templates.c
@@ -1385,13 +1385,13 @@ gboolean
 init_composer_actions (GtkUIManager *ui_manager,
                        EMsgComposer *composer)
 {
-       GtkhtmlEditor *editor;
+       EHTMLEditor *editor;
 
-       editor = GTKHTML_EDITOR (composer);
+       editor = e_msg_composer_get_editor (composer);
 
        /* Add actions to the "composer" action group. */
        gtk_action_group_add_actions (
-               gtkhtml_editor_get_action_group (editor, "composer"),
+               e_html_editor_get_action_group (editor, "composer"),
                composer_entries, G_N_ELEMENTS (composer_entries), composer);
 
        return TRUE;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 62d6c6a..36c896f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -212,9 +212,12 @@ e-util/e-cell-text.c
 e-util/e-charset-combo-box.c
 e-util/e-charset.c
 e-util/e-client-cache.c
+e-util/e-color-chooser-widget.c
+e-util/e-color-combo.c
 e-util/e-dateedit.c
 e-util/e-datetime-format.c
 e-util/e-dialog-utils.c
+e-util/e-emoticon-chooser.c
 e-util/e-file-utils.c
 e-util/e-filter-datespec.c
 e-util/e-filter-file.c
@@ -223,6 +226,20 @@ e-util/e-filter-option.c
 e-util/e-filter-part.c
 e-util/e-filter-rule.c
 e-util/e-focus-tracker.c
+e-util/e-html-editor-actions.c
+e-util/e-html-editor-cell-dialog.c
+e-util/e-html-editor-find-dialog.c
+e-util/e-html-editor-hrule-dialog.c
+e-util/e-html-editor-image-dialog.c
+e-util/e-html-editor-link-dialog.c
+e-util/e-html-editor-page-dialog.c
+e-util/e-html-editor-paragraph-dialog.c
+e-util/e-html-editor-replace-dialog.c
+e-util/e-html-editor-spell-check-dialog.c
+e-util/e-html-editor-table-dialog.c
+e-util/e-html-editor-text-dialog.c
+e-util/e-html-editor-view.c
+e-util/e-html-editor.c
 e-util/e-image-chooser.c
 e-util/e-import-assistant.c
 e-util/e-interval-chooser.c
@@ -255,6 +272,7 @@ e-util/e-send-options.c
 e-util/e-source-config-dialog.c
 e-util/e-source-config.c
 e-util/e-source-selector-dialog.c
+e-util/e-spell-dictionary.c
 e-util/e-spell-entry.c
 e-util/e-system.error.xml
 e-util/e-table-click-to-add.c
@@ -277,7 +295,6 @@ e-util/e-timezone-dialog.c
 e-util/e-tree-selection-model.c
 e-util/e-tree.c
 e-util/e-url-entry.c
-e-util/e-web-view-gtkhtml.c
 e-util/e-web-view.c
 e-util/e-widget-undo.c
 e-util/ea-calendar-item.c
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 5abf2e7..37818a4 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -2,3 +2,4 @@ data/evolution-alarm-notify.desktop.in
 data/evolution.desktop.in
 designs/OOA/ooa.ui
 designs/read_receipts/read.ui
+e-util/test-html-editor.c
diff --git a/shell/Makefile.am b/shell/Makefile.am
index 686b8c3..29fdb27 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -61,9 +61,9 @@ libevolution_shell_la_CPPFLAGS =                              \
        -DG_LOG_DOMAIN=\"evolution-shell\"                      \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(CLUTTER_GTK_CFLAGS)                                   \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 libevolution_shell_la_SOURCES =                        \
        $(evolution_shell_include_HEADERS)      \
@@ -122,9 +122,9 @@ evolution_CPPFLAGS =                                                \
        -DLIBDIR=\""$(datadir)"\"                               \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                         \
        $(GNOME_PLATFORM_CFLAGS)                                \
-       $(GTKHTML_CFLAGS)                                       \
        $(CLUTTER_GTK_CFLAGS)                                   \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                                 \
+       $(NULL)
 
 evolution_SOURCES =                            \
        main.c                                  \
diff --git a/smime/gui/Makefile.am b/smime/gui/Makefile.am
index 5d3f51a..c71ac45 100644
--- a/smime/gui/Makefile.am
+++ b/smime/gui/Makefile.am
@@ -17,9 +17,9 @@ libevolution_smime_la_CPPFLAGS =                      \
        -DPREFIX=\""$(prefix)"\"                        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
        $(CERT_UI_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 libevolution_smime_la_SOURCES =        \
        ca-trust-dialog.c               \
@@ -40,8 +40,8 @@ libevolution_smime_la_LIBADD =                                \
        $(top_builddir)/smime/lib/libessmime.la         \
        $(EVOLUTION_DATA_SERVER_LIBS)                   \
        $(GNOME_PLATFORM_LIBS)                          \
-       $(GTKHTML_LIBS)                                 \
-       $(CERT_UI_LIBS)
+       $(CERT_UI_LIBS)                                 \
+       $(NULL)
 
 libevolution_smime_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
 
diff --git a/smime/lib/Makefile.am b/smime/lib/Makefile.am
index 811c778..6e7264e 100644
--- a/smime/lib/Makefile.am
+++ b/smime/lib/Makefile.am
@@ -14,9 +14,9 @@ libessmime_la_CPPFLAGS =                              \
        -DPREFIX=\""$(prefix)"\"                        \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
-       $(GTKHTML_CFLAGS)                               \
        $(CERT_UI_CFLAGS)                               \
-       $(CODE_COVERAGE_CFLAGS)
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
 
 libessmime_la_SOURCES =        \
        e-cert.c                \
@@ -32,8 +32,8 @@ libessmime_la_LIBADD =                                \
        $(top_builddir)/e-util/libevolution-util.la \
        $(EVOLUTION_DATA_SERVER_LIBS)           \
        $(GNOME_PLATFORM_LIBS)                  \
-       $(GTKHTML_LIBS)                         \
-       $(CERT_UI_LIBS)
+       $(CERT_UI_LIBS)                         \
+       $(NULL)
 
 libessmime_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
 


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