[evolution] Bug 724651 - [webkit-composer] Undo or Redo does not work



commit 13c957f614618d7fed8938ff0da70361eb4b11f4
Author: Tomas Popela <tpopela redhat com>
Date:   Thu Mar 12 14:40:22 2015 +0100

    Bug 724651 - [webkit-composer] Undo or Redo does not work
    
    Implement the undo and redo operations in EHTMLEditorView. We have
    to implement all the stuff ourselves as there was nothing that we
    can reuse on WebKit side (at least not now).

 e-util/e-html-editor-actions.c        |  154 +++-
 e-util/e-html-editor-cell-dialog.c    |   64 +
 e-util/e-html-editor-hrule-dialog.c   |   42 +
 e-util/e-html-editor-image-dialog.c   |   44 +
 e-util/e-html-editor-page-dialog.c    |   52 +
 e-util/e-html-editor-replace-dialog.c |   29 +-
 e-util/e-html-editor-selection.c      |  671 +++++++++-
 e-util/e-html-editor-selection.h      |    6 +
 e-util/e-html-editor-table-dialog.c   |   40 +
 e-util/e-html-editor-view.c           | 2205 ++++++++++++++++++++++++++++++++-
 e-util/e-html-editor-view.h           |   85 ++
 11 files changed, 3293 insertions(+), 99 deletions(-)
---
diff --git a/e-util/e-html-editor-actions.c b/e-util/e-html-editor-actions.c
index 98476a5..c5235b8 100644
--- a/e-util/e-html-editor-actions.c
+++ b/e-util/e-html-editor-actions.c
@@ -135,9 +135,50 @@ editor_update_static_spell_actions (EHTMLEditor *editor)
  *****************************************************************************/
 
 static void
+prepare_history_for_table (EHTMLEditor *editor,
+                           WebKitDOMElement *table,
+                           EHTMLEditorViewHistoryEvent *ev)
+{
+       EHTMLEditorSelection *selection;
+       EHTMLEditorView *view;
+
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       ev->type = HISTORY_TABLE_DIALOG;
+
+       e_html_editor_selection_get_selection_coordinates (
+               selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+       ev->data.dom.from = webkit_dom_node_clone_node (
+               WEBKIT_DOM_NODE (table), TRUE);
+}
+
+static void
+save_history_for_table (EHTMLEditor *editor,
+                        WebKitDOMElement *table,
+                        EHTMLEditorViewHistoryEvent *ev)
+{
+       EHTMLEditorSelection *selection;
+       EHTMLEditorView *view;
+
+       view = e_html_editor_get_view (editor);
+       selection = e_html_editor_view_get_selection (view);
+
+       if (table)
+               ev->data.dom.to = webkit_dom_node_clone_node (
+                       WEBKIT_DOM_NODE (table), TRUE);
+       else
+               ev->data.dom.to = NULL;
+       e_html_editor_selection_get_selection_coordinates (
+               selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
+       e_html_editor_view_insert_new_history_event (view, ev);
+}
+
+static void
 action_context_delete_cell_contents_cb (GtkAction *action,
                                         EHTMLEditor *editor)
 {
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        WebKitDOMNode *node;
        WebKitDOMElement *cell, *table;
 
@@ -153,15 +194,20 @@ action_context_delete_cell_contents_cb (GtkAction *action,
        table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
        g_return_if_fail (table != NULL);
 
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       prepare_history_for_table (editor, table, ev);
 
        while ((node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (cell))))
                remove_node (node);
+
+       save_history_for_table (editor, table, ev);
 }
 
 static void
 action_context_delete_column_cb (GtkAction *action,
                                  EHTMLEditor *editor)
 {
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        WebKitDOMElement *cell, *table;
        WebKitDOMHTMLCollection *rows;
        gulong index, length, ii;
@@ -179,6 +225,9 @@ action_context_delete_column_cb (GtkAction *action,
        table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
        g_return_if_fail (table != NULL);
 
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       prepare_history_for_table (editor, table, ev);
+
        rows = webkit_dom_html_table_element_get_rows (
                        WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
        length = webkit_dom_html_collection_get_length (rows);
@@ -196,22 +245,31 @@ action_context_delete_column_cb (GtkAction *action,
                g_object_unref (row);
        }
        g_object_unref (rows);
+
+       save_history_for_table (editor, table, ev);
 }
 
 static void
 action_context_delete_row_cb (GtkAction *action,
                               EHTMLEditor *editor)
 {
-       WebKitDOMElement *row;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
+       WebKitDOMElement *row, *table;
 
        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);
+       table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       prepare_history_for_table (editor, table, ev);
+
+       remove_node (WEBKIT_DOM_NODE (row));
+
+       save_history_for_table (editor, table, ev);
 }
 
 static void
@@ -219,22 +277,27 @@ action_context_delete_table_cb (GtkAction *action,
                                 EHTMLEditor *editor)
 {
        WebKitDOMElement *table;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
 
        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);
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       prepare_history_for_table (editor, table, ev);
+
+       remove_node (WEBKIT_DOM_NODE (table));
+
+       save_history_for_table (editor, NULL, ev);
 }
 
 static void
 action_context_insert_column_after_cb (GtkAction *action,
                                        EHTMLEditor *editor)
 {
-       WebKitDOMElement *cell, *row;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
+       WebKitDOMElement *cell, *row, *table;
        gulong index;
 
        g_return_if_fail (editor->priv->table_cell != NULL);
@@ -249,6 +312,12 @@ action_context_insert_column_after_cb (GtkAction *action,
        row = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR");
        g_return_if_fail (row != NULL);
 
+       table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       prepare_history_for_table (editor, table, ev);
+
        /* Get the first row in the table */
        row = WEBKIT_DOM_ELEMENT (
                webkit_dom_node_get_first_child (
@@ -264,13 +333,16 @@ action_context_insert_column_after_cb (GtkAction *action,
                row = WEBKIT_DOM_ELEMENT (
                        webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
        }
+
+       save_history_for_table (editor, table, ev);
 }
 
 static void
 action_context_insert_column_before_cb (GtkAction *action,
                                         EHTMLEditor *editor)
 {
-       WebKitDOMElement *cell, *row;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
+       WebKitDOMElement *cell, *row, *table;
        gulong index;
 
        g_return_if_fail (editor->priv->table_cell != NULL);
@@ -285,6 +357,12 @@ action_context_insert_column_before_cb (GtkAction *action,
        row = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR");
        g_return_if_fail (row != NULL);
 
+       table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       prepare_history_for_table (editor, table, ev);
+
        /* Get the first row in the table */
        row = WEBKIT_DOM_ELEMENT (
                webkit_dom_node_get_first_child (
@@ -300,12 +378,15 @@ action_context_insert_column_before_cb (GtkAction *action,
                row = WEBKIT_DOM_ELEMENT (
                        webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
        }
+
+       save_history_for_table (editor, table, ev);
 }
 
 static void
 action_context_insert_row_above_cb (GtkAction *action,
                                     EHTMLEditor *editor)
 {
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        WebKitDOMElement *row, *table;
        WebKitDOMHTMLCollection *cells;
        WebKitDOMHTMLElement *new_row;
@@ -319,6 +400,9 @@ action_context_insert_row_above_cb (GtkAction *action,
        table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
        g_return_if_fail (table != NULL);
 
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       prepare_history_for_table (editor, table, ev);
+
        index = webkit_dom_html_table_row_element_get_row_index (
                        WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
 
@@ -333,12 +417,15 @@ action_context_insert_row_above_cb (GtkAction *action,
                        WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL);
        }
        g_object_unref (cells);
+
+       save_history_for_table (editor, table, ev);
 }
 
 static void
 action_context_insert_row_below_cb (GtkAction *action,
                                     EHTMLEditor *editor)
 {
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        WebKitDOMElement *row, *table;
        WebKitDOMHTMLCollection *cells;
        WebKitDOMHTMLElement *new_row;
@@ -352,6 +439,9 @@ action_context_insert_row_below_cb (GtkAction *action,
        table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
        g_return_if_fail (table != NULL);
 
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       prepare_history_for_table (editor, table, ev);
+
        index = webkit_dom_html_table_row_element_get_row_index (
                        WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
 
@@ -366,6 +456,8 @@ action_context_insert_row_below_cb (GtkAction *action,
                        WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL);
        }
        g_object_unref (cells);
+
+       save_history_for_table (editor, table, ev);
 }
 
 static void
@@ -431,11 +523,51 @@ action_cut_cb (GtkAction *action,
                EHTMLEditor *editor)
 {
        EHTMLEditorView *view = e_html_editor_get_view (editor);
+       EHTMLEditorSelection *selection;
+       EHTMLEditorViewHistoryEvent *ev;
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
 
        if (!gtk_widget_has_focus (GTK_WIDGET (view)))
                gtk_widget_grab_focus (GTK_WIDGET (view));
 
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       if (!webkit_dom_dom_selection_get_range_count (dom_selection))
+               return;
+
+       selection = e_html_editor_view_get_selection (view);
+
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       ev->type = HISTORY_DELETE;
+
+       printf ("HISTORY: CUT ; \n");
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       e_html_editor_selection_get_selection_coordinates (
+               selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       if (webkit_dom_range_get_collapsed (range, NULL)) {
+               g_warning ("THIS SHOULD NOT HAPPEN!\n");
+       } else {
+               fragment = webkit_dom_range_clone_contents (range, NULL);
+       }
+       /* Save the fragment. */
+       ev->data.fragment = g_object_ref (fragment);
+
        webkit_web_view_cut_clipboard (WEBKIT_WEB_VIEW (view));
+
+       e_html_editor_selection_get_selection_coordinates (
+               selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
+
+       e_html_editor_view_insert_new_history_event (view, ev);
+
+       e_html_editor_view_force_spell_check_for_current_paragraph (view);
 }
 
 static void
@@ -840,7 +972,7 @@ action_redo_cb (GtkAction *action,
        EHTMLEditorView *view = e_html_editor_get_view (editor);
 
        if (gtk_widget_has_focus (GTK_WIDGET (view)))
-               webkit_web_view_redo (WEBKIT_WEB_VIEW (view));
+               e_html_editor_view_redo (view);
 }
 
 static void
@@ -907,7 +1039,7 @@ action_undo_cb (GtkAction *action,
        EHTMLEditorView *view = e_html_editor_get_view (editor);
 
        if (gtk_widget_has_focus (GTK_WIDGET (view)))
-               webkit_web_view_undo (WEBKIT_WEB_VIEW (view));
+               e_html_editor_view_undo (view);
 }
 
 static void
diff --git a/e-util/e-html-editor-cell-dialog.c b/e-util/e-html-editor-cell-dialog.c
index a70f0ce..5ac970a 100644
--- a/e-util/e-html-editor-cell-dialog.c
+++ b/e-util/e-html-editor-cell-dialog.c
@@ -64,6 +64,8 @@ struct _EHTMLEditorCellDialogPrivate {
 
        WebKitDOMElement *cell;
        guint scope;
+
+       EHTMLEditorViewHistoryEvent *history_event;
 };
 
 enum {
@@ -501,11 +503,34 @@ html_editor_cell_dialog_remove_image (EHTMLEditorCellDialog *dialog)
 static void
 html_editor_cell_dialog_show (GtkWidget *widget)
 {
+       EHTMLEditor *editor;
        EHTMLEditorCellDialog *dialog;
+       EHTMLEditorView *view;
        gchar *tmp;
        GdkRGBA color;
 
        dialog = E_HTML_EDITOR_CELL_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               EHTMLEditorViewHistoryEvent *ev;
+               WebKitDOMElement *table;
+
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_TABLE_DIALOG;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       e_html_editor_view_get_selection (view),
+                       &ev->before.start.x, &ev->before.start.y,
+                       &ev->before.end.x, &ev->before.end.y);
+
+               table = e_html_editor_dom_node_find_parent_element (
+                               WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE");
+               ev->data.dom.from = webkit_dom_node_clone_node (
+                       WEBKIT_DOM_NODE (table), TRUE);
+               dialog->priv->history_event = ev;
+       }
 
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (dialog->priv->scope_cell_button), TRUE);
@@ -601,6 +626,44 @@ html_editor_cell_dialog_show (GtkWidget *widget)
 }
 
 static void
+html_editor_cell_dialog_hide (GtkWidget *widget)
+{
+       EHTMLEditorCellDialogPrivate *priv;
+       EHTMLEditorViewHistoryEvent *ev;
+
+       priv = E_HTML_EDITOR_CELL_DIALOG_GET_PRIVATE (widget);
+       ev = priv->history_event;
+
+       if (ev) {
+               EHTMLEditorCellDialog *dialog;
+               EHTMLEditor *editor;
+               EHTMLEditorSelection *selection;
+               EHTMLEditorView *view;
+               WebKitDOMElement *table;
+
+               dialog = E_HTML_EDITOR_CELL_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);
+
+               table = e_html_editor_dom_node_find_parent_element (
+                               WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE");
+
+               ev->data.dom.to = webkit_dom_node_clone_node (
+                       WEBKIT_DOM_NODE (table), TRUE);
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
+       g_object_unref (priv->cell);
+       priv->cell = NULL;
+
+       GTK_WIDGET_CLASS (e_html_editor_cell_dialog_parent_class)->hide (widget);
+}
+
+static void
 e_html_editor_cell_dialog_class_init (EHTMLEditorCellDialogClass *class)
 {
        GtkWidgetClass *widget_class;
@@ -609,6 +672,7 @@ e_html_editor_cell_dialog_class_init (EHTMLEditorCellDialogClass *class)
 
        widget_class = GTK_WIDGET_CLASS (class);
        widget_class->show = html_editor_cell_dialog_show;
+       widget_class->hide = html_editor_cell_dialog_hide;
 }
 
 static void
diff --git a/e-util/e-html-editor-hrule-dialog.c b/e-util/e-html-editor-hrule-dialog.c
index 1d1dedf..5e58f4e 100644
--- a/e-util/e-html-editor-hrule-dialog.c
+++ b/e-util/e-html-editor-hrule-dialog.c
@@ -43,6 +43,8 @@ struct _EHTMLEditorHRuleDialogPrivate {
        GtkWidget *shaded_check;
 
        WebKitDOMHTMLHRElement *hr_element;
+
+       EHTMLEditorViewHistoryEvent *history_event;
 };
 
 G_DEFINE_TYPE (
@@ -198,8 +200,32 @@ static void
 html_editor_hrule_dialog_hide (GtkWidget *widget)
 {
        EHTMLEditorHRuleDialogPrivate *priv;
+       EHTMLEditorViewHistoryEvent *ev;
 
        priv = E_HTML_EDITOR_HRULE_DIALOG_GET_PRIVATE (widget);
+       ev = priv->history_event;
+
+       if (ev) {
+               EHTMLEditorHRuleDialog *dialog;
+               EHTMLEditor *editor;
+               EHTMLEditorSelection *selection;
+               EHTMLEditorView *view;
+
+               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);
+               selection = e_html_editor_view_get_selection (view);
+
+               ev->data.dom.to = webkit_dom_node_clone_node (
+                       WEBKIT_DOM_NODE (priv->hr_element), FALSE);
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+
+               if (!ev->data.dom.from)
+                       g_object_unref (priv->hr_element);
+       }
 
        priv->hr_element = NULL;
 
@@ -221,6 +247,22 @@ html_editor_hrule_dialog_show (GtkWidget *widget)
        view = e_html_editor_get_view (editor);
        selection = e_html_editor_view_get_selection (view);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               EHTMLEditorViewHistoryEvent *ev;
+
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_HRULE_DIALOG;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
+               if (dialog->priv->hr_element)
+                       ev->data.dom.from = webkit_dom_node_clone_node (
+                               WEBKIT_DOM_NODE (dialog->priv->hr_element), FALSE);
+               else
+                       ev->data.dom.from = NULL;
+               dialog->priv->history_event = ev;
+       }
+
        if (!dialog->priv->hr_element) {
                WebKitDOMElement *selection_start, *parent, *rule;
 
diff --git a/e-util/e-html-editor-image-dialog.c b/e-util/e-html-editor-image-dialog.c
index c2918ce..62ffa95 100644
--- a/e-util/e-html-editor-image-dialog.c
+++ b/e-util/e-html-editor-image-dialog.c
@@ -52,6 +52,8 @@ struct _EHTMLEditorImageDialogPrivate {
        GtkWidget *test_url_button;
 
        WebKitDOMHTMLImageElement *image;
+
+       EHTMLEditorViewHistoryEvent *history_event;
 };
 
 G_DEFINE_TYPE (
@@ -363,6 +365,9 @@ static void
 html_editor_image_dialog_show (GtkWidget *widget)
 {
        EHTMLEditorImageDialog *dialog;
+       EHTMLEditor *editor;
+       EHTMLEditorSelection *selection;
+       EHTMLEditorView *view;
        WebKitDOMElement *link;
        gchar *tmp;
        glong val;
@@ -373,6 +378,23 @@ html_editor_image_dialog_show (GtkWidget *widget)
                return;
        }
 
+       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);
+
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               EHTMLEditorViewHistoryEvent *ev;
+
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_IMAGE_DIALOG;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
+               ev->data.dom.from = webkit_dom_node_clone_node (
+                       WEBKIT_DOM_NODE (dialog->priv->image), FALSE);
+               dialog->priv->history_event = ev;
+       }
+
        tmp = webkit_dom_element_get_attribute (
                WEBKIT_DOM_ELEMENT (dialog->priv->image), "data-uri");
        if (tmp && *tmp) {
@@ -435,9 +457,31 @@ static void
 html_editor_image_dialog_hide (GtkWidget *widget)
 {
        EHTMLEditorImageDialogPrivate *priv;
+       EHTMLEditorViewHistoryEvent *ev;
 
        priv = E_HTML_EDITOR_IMAGE_DIALOG_GET_PRIVATE (widget);
+       ev = priv->history_event;
+
+       if (ev) {
+               EHTMLEditorImageDialog *dialog;
+               EHTMLEditor *editor;
+               EHTMLEditorSelection *selection;
+               EHTMLEditorView *view;
+
+               dialog = E_HTML_EDITOR_IMAGE_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);
+
+               ev->data.dom.to = webkit_dom_node_clone_node (
+                       WEBKIT_DOM_NODE (priv->image), FALSE);
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
 
+       g_object_unref (priv->image);
        priv->image = NULL;
 
        GTK_WIDGET_CLASS (e_html_editor_image_dialog_parent_class)->hide (widget);
diff --git a/e-util/e-html-editor-page-dialog.c b/e-util/e-html-editor-page-dialog.c
index 1695589..ec75e80 100644
--- a/e-util/e-html-editor-page-dialog.c
+++ b/e-util/e-html-editor-page-dialog.c
@@ -43,6 +43,8 @@ struct _EHTMLEditorPageDialogPrivate {
        GtkWidget *background_image_filechooser;
 
        GtkWidget *remove_image_button;
+
+       EHTMLEditorViewHistoryEvent *history_event;
 };
 
 typedef struct _Template {
@@ -329,6 +331,20 @@ html_editor_page_dialog_show (GtkWidget *widget)
        document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
        body = webkit_dom_document_get_body (document);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               EHTMLEditorSelection *selection;
+               EHTMLEditorViewHistoryEvent *ev;
+
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_PAGE_DIALOG;
+
+               selection = e_html_editor_view_get_selection (view);
+               e_html_editor_selection_get_selection_coordinates (
+                       selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
+               ev->data.dom.from = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), FALSE);
+               dialog->priv->history_event = ev;
+       }
+
        tmp = webkit_dom_element_get_attribute (
                WEBKIT_DOM_ELEMENT (body), "data-uri");
        if (tmp && *tmp) {
@@ -399,6 +415,41 @@ html_editor_page_dialog_show (GtkWidget *widget)
 }
 
 static void
+html_editor_page_dialog_hide (GtkWidget *widget)
+{
+       EHTMLEditorPageDialogPrivate *priv;
+       EHTMLEditorViewHistoryEvent *ev;
+
+       priv = E_HTML_EDITOR_PAGE_DIALOG_GET_PRIVATE (widget);
+       ev = priv->history_event;
+
+       if (ev) {
+               EHTMLEditorPageDialog *dialog;
+               EHTMLEditor *editor;
+               EHTMLEditorSelection *selection;
+               EHTMLEditorView *view;
+               WebKitDOMDocument *document;
+               WebKitDOMHTMLElement *body;
+
+               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);
+               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);
+
+               ev->data.dom.to = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), FALSE);
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
+       GTK_WIDGET_CLASS (e_html_editor_page_dialog_parent_class)->hide (widget);
+}
+
+static void
 e_html_editor_page_dialog_class_init (EHTMLEditorPageDialogClass *class)
 {
        GtkWidgetClass *widget_class;
@@ -407,6 +458,7 @@ e_html_editor_page_dialog_class_init (EHTMLEditorPageDialogClass *class)
 
        widget_class = GTK_WIDGET_CLASS (class);
        widget_class->show = html_editor_page_dialog_show;
+       widget_class->hide = html_editor_page_dialog_hide;
 }
 
 static void
diff --git a/e-util/e-html-editor-replace-dialog.c b/e-util/e-html-editor-replace-dialog.c
index ce1a0b7..6c594c2 100644
--- a/e-util/e-html-editor-replace-dialog.c
+++ b/e-util/e-html-editor-replace-dialog.c
@@ -61,15 +61,15 @@ static gboolean
 jump (EHTMLEditorReplaceDialog *dialog)
 {
        EHTMLEditor *editor;
-       WebKitWebView *webview;
+       WebKitWebView *web_view;
        gboolean found;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       webview = WEBKIT_WEB_VIEW (
+       web_view = WEBKIT_WEB_VIEW (
                        e_html_editor_get_view (editor));
 
        found = webkit_web_view_search_text (
-               webview,
+               web_view,
                gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry)),
                gtk_toggle_button_get_active (
                        GTK_TOGGLE_BUTTON (dialog->priv->case_sensitive)),
@@ -128,13 +128,15 @@ html_editor_replace_dialog_replace_all_cb (EHTMLEditorReplaceDialog *dialog)
        gchar *result;
        EHTMLEditor *editor;
        EHTMLEditorView *view;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        EHTMLEditorSelection *selection;
-       const gchar *replacement;
+       const gchar *replacement, *search_text;
 
        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));
+       search_text = gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry));
 
        while (jump (dialog)) {
                e_html_editor_selection_replace (selection, replacement);
@@ -145,9 +147,22 @@ html_editor_replace_dialog_replace_all_cb (EHTMLEditorReplaceDialog *dialog)
                        selection, TRUE, E_HTML_EDITOR_SELECTION_GRANULARITY_WORD);
        }
 
-       result = g_strdup_printf (ngettext("%d occurence replaced", 
-                                          "%d occurences replaced", 
-                                          i), 
+       if (i != 0) {
+               if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+                       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+                       ev->type = HISTORY_REPLACE_ALL;
+
+                       ev->data.string.from = g_strdup (search_text);
+                       ev->data.string.to = g_strdup (replacement);
+
+                       e_html_editor_view_insert_new_history_event (view, ev);
+               }
+               e_html_editor_view_force_spell_check (view);
+       }
+
+       result = g_strdup_printf (ngettext("%d occurence replaced",
+                                          "%d occurences replaced",
+                                          i),
                                  i);
        gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), result);
        gtk_widget_show (dialog->priv->result_label);
diff --git a/e-util/e-html-editor-selection.c b/e-util/e-html-editor-selection.c
index 4522d3d..1335ef6 100644
--- a/e-util/e-html-editor-selection.c
+++ b/e-util/e-html-editor-selection.c
@@ -1053,15 +1053,53 @@ e_html_editor_selection_replace (EHTMLEditorSelection *selection,
                                  const gchar *new_string)
 {
        EHTMLEditorView *view;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
 
        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);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               WebKitDOMDocument *document;
+               WebKitDOMDOMWindow *window;
+               WebKitDOMDOMSelection *dom_selection;
+               WebKitDOMRange *range;
+
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_REPLACE;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               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);
+               ev->data.string.from = webkit_dom_range_get_text (range);
+               ev->data.string.to = g_strdup (new_string);
+       }
+
        e_html_editor_view_exec_command (
                view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, new_string);
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
+       e_html_editor_view_force_spell_check_for_current_paragraph (view);
+
        g_object_unref (view);
 }
 
@@ -1365,6 +1403,86 @@ get_parent_block_node_from_child (WebKitDOMNode *node)
        return parent;
 }
 
+void
+e_html_editor_selection_get_selection_coordinates (EHTMLEditorSelection *selection,
+                                                   guint *start_x,
+                                                   guint *start_y,
+                                                   guint *end_x,
+                                                   guint *end_y)
+{
+       EHTMLEditorView *view;
+       gboolean created_selection_markers = FALSE;
+       guint local_x = 0, local_y = 0;
+       WebKitDOMElement *element, *parent;
+       WebKitDOMDocument *document;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
+       g_return_if_fail (start_x != NULL);
+       g_return_if_fail (start_y != NULL);
+       g_return_if_fail (end_x != NULL);
+       g_return_if_fail (end_y != NULL);
+
+       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-selection-start-marker");
+       if (!element) {
+               created_selection_markers = TRUE;
+               e_html_editor_selection_save (selection);
+               element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               if (!element)
+                       return;
+       }
+
+       parent = element;
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               local_x += (guint) webkit_dom_element_get_offset_left (parent);
+               local_y += (guint) webkit_dom_element_get_offset_top (parent);
+               parent = webkit_dom_element_get_offset_parent (parent);
+       }
+
+       if (start_x)
+               *start_x = local_x;
+       if (start_y)
+               *start_y = local_y;
+
+       if (e_html_editor_selection_is_collapsed (selection)) {
+               *end_x = local_x;
+               *end_y = local_y;
+
+               if (created_selection_markers)
+                       e_html_editor_selection_restore (selection);
+
+               return;
+       }
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+
+       local_x = 0;
+       local_y = 0;
+
+       parent = element;
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               local_x += (guint) webkit_dom_element_get_offset_left (parent);
+               local_y += (guint) webkit_dom_element_get_offset_top (parent);
+               parent = webkit_dom_element_get_offset_parent (parent);
+       }
+
+       if (end_x)
+               *end_x = local_x;
+       if (end_y)
+               *end_y = local_y;
+
+       if (created_selection_markers)
+               e_html_editor_selection_restore (selection);
+}
+
 /**
  * e_html_editor_selection_set_alignment:
  * @selection: an #EHTMLEditorSelection
@@ -1376,7 +1494,9 @@ void
 e_html_editor_selection_set_alignment (EHTMLEditorSelection *selection,
                                        EHTMLEditorSelectionAlignment alignment)
 {
+       EHTMLEditorSelectionAlignment current_alignment;
        EHTMLEditorView *view;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        gboolean after_selection_end = FALSE;
        const gchar *class = "", *list_class = "";
        WebKitDOMDocument *document;
@@ -1385,9 +1505,14 @@ e_html_editor_selection_set_alignment (EHTMLEditorSelection *selection,
 
        g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
 
-       if (e_html_editor_selection_get_alignment (selection) == alignment)
+       current_alignment = e_html_editor_selection_get_alignment (selection);
+
+       if (current_alignment == alignment)
                return;
 
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
        switch (alignment) {
                case E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER:
                        class = "-x-evo-align-center";
@@ -1405,13 +1530,24 @@ e_html_editor_selection_set_alignment (EHTMLEditorSelection *selection,
 
        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);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_ALIGNMENT;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.style.from = current_alignment;
+               ev->data.style.to = alignment;
+       }
+
        selection_start_marker = webkit_dom_document_query_selector (
                document, "span#-x-evo-selection-start-marker", NULL);
        selection_end_marker = webkit_dom_document_query_selector (
@@ -1483,6 +1619,16 @@ e_html_editor_selection_set_alignment (EHTMLEditorSelection *selection,
                block = next_block;
        }
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        e_html_editor_selection_restore (selection);
        e_html_editor_view_force_spell_check_for_current_paragraph (view);
 
@@ -2417,6 +2563,7 @@ e_html_editor_selection_set_block_format (EHTMLEditorSelection *selection,
 {
        EHTMLEditorView *view;
        EHTMLEditorSelectionBlockFormat current_format;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        const gchar *value;
        gboolean from_list = FALSE, to_list = FALSE, html_mode;
        WebKitDOMDocument *document;
@@ -2496,6 +2643,20 @@ e_html_editor_selection_set_block_format (EHTMLEditorSelection *selection,
                return;
        }
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_BLOCK_FORMAT;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.style.from = current_format;
+               ev->data.style.to = format;
+       }
+
        if (from_list && to_list)
                format_change_list_to_list (selection, format, document, html_mode);
 
@@ -2515,6 +2676,16 @@ e_html_editor_selection_set_block_format (EHTMLEditorSelection *selection,
 
        e_html_editor_view_set_changed (view, TRUE);
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        g_object_unref (view);
 
        g_object_notify (G_OBJECT (selection), "block-format");
@@ -2581,6 +2752,7 @@ e_html_editor_selection_set_font_color (EHTMLEditorSelection *selection,
 {
        EHTMLEditorView *view;
        EHTMLEditorViewCommand command;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        guint32 rgba_value;
        gchar *color;
 
@@ -2596,11 +2768,36 @@ e_html_editor_selection_set_font_color (EHTMLEditorSelection *selection,
 
        command = E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR;
        color = g_strdup_printf ("#%06x", rgba_value);
+
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_FONT_COLOR;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.string.from = g_strdup (selection->priv->font_color);
+               ev->data.string.to = g_strdup (color);
+       }
+
        g_free (selection->priv->font_color);
        selection->priv->font_color = g_strdup (color);
        e_html_editor_view_exec_command (view, command, color);
        g_free (color);
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        g_object_unref (view);
 
        g_object_notify (G_OBJECT (selection), "font-color");
@@ -2692,6 +2889,72 @@ e_html_editor_selection_get_font_size (EHTMLEditorSelection *selection)
        return size_int;
 }
 
+static WebKitDOMElement *
+set_font_style (WebKitDOMDocument *document,
+                const gchar *element_name,
+                gboolean value)
+{
+       WebKitDOMElement *element;
+       WebKitDOMNode *parent;
+
+       element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-end-marker");
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+       if (value) {
+               WebKitDOMNode *node;
+               WebKitDOMElement *el;
+               gchar *name;
+
+               el = webkit_dom_document_create_element (document, element_name, NULL);
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (el), UNICODE_ZERO_WIDTH_SPACE, NULL);
+
+               node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (el), node, NULL);
+               name = webkit_dom_node_get_local_name (parent);
+               if (g_strcmp0 (name, element_name) == 0)
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               WEBKIT_DOM_NODE (el),
+                               webkit_dom_node_get_next_sibling (parent),
+                               NULL);
+               else
+                       webkit_dom_node_insert_before (
+                               parent,
+                               WEBKIT_DOM_NODE (el),
+                               WEBKIT_DOM_NODE (element),
+                               NULL);
+               g_free (name);
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (el), WEBKIT_DOM_NODE (element), NULL);
+
+               return el;
+       } else {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (parent),
+                       WEBKIT_DOM_NODE (element),
+                       webkit_dom_node_get_next_sibling (parent),
+                       NULL);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (parent),
+                       WEBKIT_DOM_NODE (node),
+                       webkit_dom_node_get_next_sibling (parent),
+                       NULL);
+
+               webkit_dom_html_element_insert_adjacent_text (
+                       WEBKIT_DOM_HTML_ELEMENT (parent),
+                       "afterend",
+                       UNICODE_ZERO_WIDTH_SPACE,
+                       NULL);
+       }
+
+       return NULL;
+}
+
 /**
  * e_html_editor_selection_set_font_size:
  * @selection: an #EHTMLEditorSelection
@@ -2706,18 +2969,54 @@ e_html_editor_selection_set_font_size (EHTMLEditorSelection *selection,
 {
        EHTMLEditorView *view;
        EHTMLEditorViewCommand command;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        gchar *size_str;
+       guint current_font_size;
 
        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);
 
+       current_font_size = e_html_editor_selection_get_font_size (selection);
+       if (current_font_size == font_size)
+               return;
+
+       e_html_editor_selection_save (selection);
+
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_FONT_SIZE;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.style.from = current_font_size;
+               ev->data.style.to = font_size;
+       }
+
        selection->priv->font_size = font_size;
-       command = E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE;
        size_str = g_strdup_printf ("%d", font_size);
+
+       if (e_html_editor_selection_is_collapsed (selection)) {
+               WebKitDOMElement *font;
+               WebKitDOMDocument *document;
+
+               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+               font = set_font_style (document, "font", font_size != 3);
+               if (font)
+                       webkit_dom_element_set_attribute (font, "size", size_str, NULL);
+               e_html_editor_selection_restore (selection);
+               goto exit;
+       }
+
+       e_html_editor_selection_restore (selection);
+
+       command = E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE;
        e_html_editor_view_exec_command (view, command, size_str);
-       g_free (size_str);
 
        /* Text in <font size="3"></font> (size 3 is our default size) is a little
         * bit smaller than font outsize it. So move it outside of it. */
@@ -2740,6 +3039,18 @@ e_html_editor_selection_set_font_size (EHTMLEditorSelection *selection,
                        remove_node (WEBKIT_DOM_NODE (element));
                }
        }
+ exit:
+       g_free (size_str);
+
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
 
        g_object_unref (view);
 
@@ -2993,6 +3304,7 @@ void
 e_html_editor_selection_indent (EHTMLEditorSelection *selection)
 {
        EHTMLEditorView *view;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        gboolean after_selection_start = FALSE, after_selection_end = FALSE;
        WebKitDOMDocument *document;
        WebKitDOMElement *selection_start_marker, *selection_end_marker;
@@ -3007,6 +3319,20 @@ e_html_editor_selection_indent (EHTMLEditorSelection *selection)
 
        e_html_editor_selection_save (selection);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_INDENT;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.style.from = 1;
+               ev->data.style.to = 1;
+       }
+
        selection_start_marker = webkit_dom_document_query_selector (
                document, "span#-x-evo-selection-start-marker", NULL);
 
@@ -3121,6 +3447,16 @@ e_html_editor_selection_indent (EHTMLEditorSelection *selection)
                        block = next_block;
        }
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        e_html_editor_selection_restore (selection);
        e_html_editor_view_force_spell_check_for_current_paragraph (view);
 
@@ -3324,6 +3660,7 @@ void
 e_html_editor_selection_unindent (EHTMLEditorSelection *selection)
 {
        EHTMLEditorView *view;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        gboolean after_selection_start = FALSE, after_selection_end = FALSE;
        WebKitDOMDocument *document;
        WebKitDOMElement *selection_start_marker, *selection_end_marker;
@@ -3334,6 +3671,18 @@ e_html_editor_selection_unindent (EHTMLEditorSelection *selection)
        view = e_html_editor_selection_ref_html_editor_view (selection);
        g_return_if_fail (view != NULL);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_INDENT;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+       }
+
        document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
 
        e_html_editor_selection_save (selection);
@@ -3432,6 +3781,16 @@ e_html_editor_selection_unindent (EHTMLEditorSelection *selection)
                block = next_block;
        }
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        e_html_editor_selection_restore (selection);
        e_html_editor_view_force_spell_check_for_current_paragraph (view);
 
@@ -3507,6 +3866,78 @@ e_html_editor_selection_is_bold (EHTMLEditorSelection *selection)
        return ret_val;
 }
 
+
+static void
+html_editor_selection_set_font_style (EHTMLEditorSelection *selection,
+                                      EHTMLEditorViewCommand command,
+                                      gboolean value)
+{
+       EHTMLEditorView *view;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
+
+       view = e_html_editor_selection_ref_html_editor_view (selection);
+       g_return_if_fail (view != NULL);
+
+       e_html_editor_selection_save (selection);
+
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               if (command == E_HTML_EDITOR_VIEW_COMMAND_BOLD)
+                       ev->type = HISTORY_BOLD;
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_ITALIC)
+                       ev->type = HISTORY_ITALIC;
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE)
+                       ev->type = HISTORY_UNDERLINE;
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH)
+                       ev->type = HISTORY_STRIKETHROUGH;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.style.from = !value;
+               ev->data.style.to = value;
+       }
+
+       if (e_html_editor_selection_is_collapsed (selection)) {
+               WebKitDOMDocument *document;
+               const gchar *element_name;
+
+               if (command == E_HTML_EDITOR_VIEW_COMMAND_BOLD)
+                       element_name = "b";
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_ITALIC)
+                       element_name = "i";
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE)
+                       element_name = "u";
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH)
+                       element_name = "strike";
+
+               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+               set_font_style (document, element_name, value);
+               e_html_editor_selection_restore (selection);
+
+               goto exit;
+       }
+       e_html_editor_selection_restore (selection);
+
+       e_html_editor_view_exec_command (view, command, NULL);
+ exit:
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
+       e_html_editor_view_force_spell_check_for_current_paragraph (view);
+
+       g_object_unref (view);
+}
 /**
  * e_html_editor_selection_set_bold:
  * @selection: an #EHTMLEditorSelection
@@ -3519,9 +3950,6 @@ 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)
@@ -3529,15 +3957,8 @@ e_html_editor_selection_set_bold (EHTMLEditorSelection *selection,
 
        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);
-
-       e_html_editor_view_force_spell_check_for_current_paragraph (view);
-
-       g_object_unref (view);
+       html_editor_selection_set_font_style (
+               selection, E_HTML_EDITOR_VIEW_COMMAND_BOLD, bold);
 
        g_object_notify (G_OBJECT (selection), "bold");
 }
@@ -3621,9 +4042,6 @@ 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)
@@ -3631,15 +4049,8 @@ e_html_editor_selection_set_italic (EHTMLEditorSelection *selection,
 
        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);
-
-       e_html_editor_view_force_spell_check_for_current_paragraph (view);
-
-       g_object_unref (view);
+       html_editor_selection_set_font_style (
+               selection, E_HTML_EDITOR_VIEW_COMMAND_ITALIC, italic);
 
        g_object_notify (G_OBJECT (selection), "italic");
 }
@@ -3746,6 +4157,7 @@ e_html_editor_selection_set_monospaced (EHTMLEditorSelection *selection,
                                         gboolean monospaced)
 {
        EHTMLEditorView *view;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        WebKitDOMDocument *document;
        WebKitDOMRange *range;
        WebKitDOMDOMWindow *window;
@@ -3765,6 +4177,19 @@ e_html_editor_selection_set_monospaced (EHTMLEditorSelection *selection,
        view = e_html_editor_selection_ref_html_editor_view (selection);
        g_return_if_fail (view != NULL);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_MONOSPACE;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.style.from = !monospaced;
+               ev->data.style.to = monospaced;
+       }
 
        document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
        window = webkit_dom_document_get_default_view (document);
@@ -3963,6 +4388,16 @@ e_html_editor_selection_set_monospaced (EHTMLEditorSelection *selection,
                        e_html_editor_selection_set_font_size (selection, font_size);
        }
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        e_html_editor_view_force_spell_check_for_current_paragraph (view);
 
        g_object_unref (view);
@@ -4049,9 +4484,6 @@ 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)
@@ -4059,15 +4491,8 @@ e_html_editor_selection_set_strikethrough (EHTMLEditorSelection *selection,
 
        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);
-
-       e_html_editor_view_force_spell_check_for_current_paragraph (view);
-
-       g_object_unref (view);
+       html_editor_selection_set_font_style (
+               selection, E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH, strikethrough);
 
        g_object_notify (G_OBJECT (selection), "strikethrough");
 }
@@ -4312,9 +4737,6 @@ 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)
@@ -4322,15 +4744,8 @@ e_html_editor_selection_set_underline (EHTMLEditorSelection *selection,
 
        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);
-
-       e_html_editor_view_force_spell_check_for_current_paragraph (view);
-
-       g_object_unref (view);
+       html_editor_selection_set_font_style (
+               selection, E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE, underline);
 
        g_object_notify (G_OBJECT (selection), "underline");
 }
@@ -4383,10 +4798,35 @@ e_html_editor_selection_unlink (EHTMLEditorSelection *selection)
        if (!link)
                return;
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               EHTMLEditorViewHistoryEvent *ev;
+               WebKitDOMDocumentFragment *fragment;
+
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_REMOVE_LINK;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (link), TRUE),
+                       NULL);
+               ev->data.fragment = fragment;
+
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        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);
 }
 
@@ -4429,6 +4869,7 @@ e_html_editor_selection_insert_text (EHTMLEditorSelection *selection,
                                      const gchar *plain_text)
 {
        EHTMLEditorView *view;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
 
        g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
        g_return_if_fail (plain_text != NULL);
@@ -4436,8 +4877,32 @@ e_html_editor_selection_insert_text (EHTMLEditorSelection *selection,
        view = e_html_editor_selection_ref_html_editor_view (selection);
        g_return_if_fail (view != NULL);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_PASTE;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.string.from = NULL;
+               ev->data.string.to = g_strdup (plain_text);
+       }
+
        e_html_editor_view_convert_and_insert_plain_text (view, plain_text);
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        g_object_unref (view);
 }
 
@@ -4455,6 +4920,7 @@ e_html_editor_selection_insert_html (EHTMLEditorSelection *selection,
 {
        EHTMLEditorView *view;
        EHTMLEditorViewCommand command;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
 
        g_return_if_fail (E_IS_HTML_EDITOR_SELECTION (selection));
        g_return_if_fail (html_text != NULL);
@@ -4462,6 +4928,20 @@ e_html_editor_selection_insert_html (EHTMLEditorSelection *selection,
        view = e_html_editor_selection_ref_html_editor_view (selection);
        g_return_if_fail (view != NULL);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_INSERT_HTML;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.string.from = NULL;
+               ev->data.string.to = g_strdup (html_text);
+       }
+
        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);
@@ -4475,6 +4955,16 @@ e_html_editor_selection_insert_html (EHTMLEditorSelection *selection,
                e_html_editor_view_convert_and_insert_html_to_plain_text (
                        view, html_text);
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        g_object_unref (view);
 }
 
@@ -4589,6 +5079,7 @@ insert_base64_image (EHTMLEditorSelection *selection,
                      const gchar *uri)
 {
        EHTMLEditorView *view;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        WebKitDOMDocument *document;
        WebKitDOMElement *element, *selection_start_marker, *resizable_wrapper;
        WebKitDOMText *text;
@@ -4608,6 +5099,18 @@ insert_base64_image (EHTMLEditorSelection *selection,
        selection_start_marker = webkit_dom_document_query_selector (
                document, "span#-x-evo-selection-start-marker", NULL);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_IMAGE;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+       }
+
        resizable_wrapper =
                webkit_dom_document_create_element (document, "span", NULL);
        webkit_dom_element_set_attribute (
@@ -4650,6 +5153,28 @@ insert_base64_image (EHTMLEditorSelection *selection,
 
        e_html_editor_selection_restore (selection);
        e_html_editor_view_force_spell_check_for_current_paragraph (view);
+
+       if (ev) {
+               WebKitDOMDocumentFragment *fragment;
+               WebKitDOMNode *node;
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+               node = webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (resizable_wrapper), TRUE),
+                       NULL);
+               webkit_dom_html_element_insert_adjacent_html (
+                       WEBKIT_DOM_HTML_ELEMENT (node), "afterend", "&#8203;", NULL);
+               ev->data.fragment = fragment;
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        g_object_unref (view);
 
 }
@@ -5672,7 +6197,7 @@ wrap_lines (EHTMLEditorSelection *selection,
                /* 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 */
+               /* Overwrite the current selection by the processed content */
                e_html_editor_selection_insert_html (selection, html);
 
                g_free (html);
@@ -5798,6 +6323,7 @@ void
 e_html_editor_selection_wrap_lines (EHTMLEditorSelection *selection)
 {
        EHTMLEditorView *view;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        gboolean after_selection_end = FALSE, html_mode;
        WebKitDOMDocument *document;
        WebKitDOMElement *selection_start_marker, *selection_end_marker;
@@ -5811,6 +6337,21 @@ e_html_editor_selection_wrap_lines (EHTMLEditorSelection *selection)
        document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
 
        e_html_editor_selection_save (selection);
+
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_WRAP;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.style.from = 1;
+               ev->data.style.to = 1;
+       }
+
        selection_start_marker = webkit_dom_document_query_selector (
                document, "span#-x-evo-selection-start-marker", NULL);
        selection_end_marker = webkit_dom_document_query_selector (
@@ -5868,6 +6409,16 @@ e_html_editor_selection_wrap_lines (EHTMLEditorSelection *selection)
                block = next_block;
        }
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        e_html_editor_selection_restore (selection);
 
        e_html_editor_view_force_spell_check_for_current_paragraph (view);
@@ -6073,6 +6624,16 @@ e_html_editor_selection_save (EHTMLEditorSelection *selection)
                                NULL);
                        goto insert_end_marker;
                }
+       } else if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-smiley-text")) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_get_parent_node (parent_node);
+               marker_node = webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (start_marker),
+                       webkit_dom_node_get_next_sibling (node),
+                       NULL);
+               goto insert_end_marker;
        }
 
        if (WEBKIT_DOM_IS_TEXT (container)) {
diff --git a/e-util/e-html-editor-selection.h b/e-util/e-html-editor-selection.h
index 4d0ce52..67bbe6b 100644
--- a/e-util/e-html-editor-selection.h
+++ b/e-util/e-html-editor-selection.h
@@ -260,6 +260,12 @@ EHTMLEditorSelectionAlignment
                                                (WebKitDOMNode *node);
 void           remove_wrapping_from_element    (WebKitDOMElement *element);
 void           remove_quoting_from_element     (WebKitDOMElement *element);
+void           e_html_editor_selection_get_selection_coordinates
+                                               (EHTMLEditorSelection *selection,
+                                                guint *start_x,
+                                                guint *start_y,
+                                                guint *end_x,
+                                                guint *end_y);
 G_END_DECLS
 
 #endif /* E_HTML_EDITOR_SELECTION_H */
diff --git a/e-util/e-html-editor-table-dialog.c b/e-util/e-html-editor-table-dialog.c
index 8f66e66..56a61f8 100644
--- a/e-util/e-html-editor-table-dialog.c
+++ b/e-util/e-html-editor-table-dialog.c
@@ -57,6 +57,8 @@ struct _EHTMLEditorTableDialogPrivate {
        GtkWidget *remove_image_button;
 
        WebKitDOMHTMLTableElement *table_element;
+
+       EHTMLEditorViewHistoryEvent *history_event;
 };
 
 static GdkRGBA transparent = { 0, 0, 0, 0 };
@@ -657,6 +659,22 @@ html_editor_table_dialog_show (GtkWidget *widget)
                                WEBKIT_DOM_HTML_TABLE_ELEMENT (table);
                        html_editor_table_dialog_get_values (dialog);
                }
+
+               if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+                       EHTMLEditorViewHistoryEvent *ev;
+
+                       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+                       ev->type = HISTORY_TABLE_DIALOG;
+
+                       e_html_editor_selection_get_selection_coordinates (
+                               e_html_editor_view_get_selection (view),
+                               &ev->before.start.x, &ev->before.start.y,
+                               &ev->before.end.x, &ev->before.end.y);
+                       if (table)
+                               ev->data.dom.from = webkit_dom_node_clone_node (
+                                       WEBKIT_DOM_NODE (table), TRUE);
+                       dialog->priv->history_event = ev;
+               }
        }
 
        /* Chain up to parent implementation */
@@ -679,9 +697,31 @@ static void
 html_editor_table_dialog_hide (GtkWidget *widget)
 {
        EHTMLEditorTableDialogPrivate *priv;
+       EHTMLEditorViewHistoryEvent *ev;
 
        priv = E_HTML_EDITOR_TABLE_DIALOG_GET_PRIVATE (widget);
+       ev = priv->history_event;
+
+       if (ev) {
+               EHTMLEditorTableDialog *dialog;
+               EHTMLEditor *editor;
+               EHTMLEditorSelection *selection;
+               EHTMLEditorView *view;
+
+               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);
+               selection = e_html_editor_view_get_selection (view);
+
+               ev->data.dom.to = webkit_dom_node_clone_node (
+                       WEBKIT_DOM_NODE (priv->table_element), TRUE);
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
 
+       g_object_unref (priv->table_element);
        priv->table_element = NULL;
 
        GTK_WIDGET_CLASS (e_html_editor_table_dialog_parent_class)->hide (widget);
diff --git a/e-util/e-html-editor-view.c b/e-util/e-html-editor-view.c
index 4ad198e..009aef3 100644
--- a/e-util/e-html-editor-view.c
+++ b/e-util/e-html-editor-view.c
@@ -65,8 +65,12 @@
 #define HTML_KEY_CODE_SPACE 32
 #define HTML_KEY_CODE_DELETE 46
 
+#define HISTORY_SIZE_LIMIT 30
+
 #define TRY_TO_PRESERVE_BLOCKS 0
 
+#define d(x)
+
 /**
  * EHTMLEditorView:
  *
@@ -107,10 +111,15 @@ struct _EHTMLEditorViewPrivate {
        gboolean return_key_pressed;
        gboolean space_key_pressed;
        gboolean smiley_written;
+       gboolean undo_redo_in_progress;
+       gboolean dont_save_history_in_body_input;
 
        GHashTable *old_settings;
 
        GQueue *post_reload_operations;
+
+       GList *history;
+       guint history_size;
 };
 
 enum {
@@ -193,24 +202,42 @@ html_editor_view_get_dom_range (EHTMLEditorView *view)
        return webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
 }
 
+gboolean
+e_html_editor_view_can_redo (EHTMLEditorView *view)
+{
+       if (view->priv->history && view->priv->history->prev)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+gboolean
+e_html_editor_view_can_undo (EHTMLEditorView *view)
+{
+       if (view->priv->history) {
+               EHTMLEditorViewHistoryEvent *event;
+
+               event = view->priv->history->data;
+
+               return (event->type != HISTORY_START);
+       } else
+               return FALSE;
+}
+
 static void
-html_editor_view_user_changed_contents_cb (EHTMLEditorView *view,
-                                           gpointer user_data)
+html_editor_view_user_changed_contents_cb (EHTMLEditorView *view)
 {
-       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);
+       can_redo = e_html_editor_view_can_redo (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);
+       can_undo = e_html_editor_view_can_undo (view);
        if (view->priv->can_undo != can_undo) {
                view->priv->can_undo = can_undo;
                g_object_notify (G_OBJECT (view), "can-undo");
@@ -1442,6 +1469,7 @@ emoticon_insert_span (EHTMLEditorView *view,
                       WebKitDOMElement *span)
 {
        EHTMLEditorSelection *selection;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        gboolean misplaced_selection = FALSE, empty = FALSE;
        gchar *node_text = NULL, *content;
        const gchar *emoticon_start;
@@ -1452,16 +1480,53 @@ emoticon_insert_span (EHTMLEditorView *view,
        WebKitDOMRange *range;
 
        selection = e_html_editor_view_get_selection (view);
-       if (!e_html_editor_selection_is_collapsed (selection))
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       if (e_html_editor_selection_is_collapsed (selection)) {
+               e_html_editor_selection_save (selection);
+
+               selection_start_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               selection_end_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+
+               if (!view->priv->smiley_written) {
+                       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+                               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+                               ev->type = HISTORY_SMILEY;
+
+                               e_html_editor_selection_get_selection_coordinates (
+                                       selection,
+                                       &ev->before.start.x,
+                                       &ev->before.start.y,
+                                       &ev->before.end.x,
+                                       &ev->before.end.y);
+                       }
+               }
+       } else {
+               if (!view->priv->smiley_written) {
+                       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+                               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+                               ev->type = HISTORY_SMILEY;
+
+                               e_html_editor_selection_get_selection_coordinates (
+                                       selection,
+                                       &ev->before.start.x,
+                                       &ev->before.start.y,
+                                       &ev->before.end.x,
+                                       &ev->before.end.y);
+                       }
+               }
+
                e_html_editor_view_exec_command (
                        view, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
 
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       e_html_editor_selection_save (selection);
-       selection_start_marker = webkit_dom_document_get_element_by_id (
-               document, "-x-evo-selection-start-marker");
-       selection_end_marker = webkit_dom_document_get_element_by_id (
-               document, "-x-evo-selection-end-marker");
+               e_html_editor_selection_save (selection);
+
+               selection_start_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               selection_end_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+       }
 
        /* If the selection was not saved, move it into the first child of body */
        if (!selection_start_marker || !selection_end_marker) {
@@ -1476,6 +1541,13 @@ emoticon_insert_span (EHTMLEditorView *view,
                        WEBKIT_DOM_ELEMENT (child),
                        &selection_start_marker,
                        &selection_end_marker);
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
        }
 
        /* Sometimes selection end marker is in body. Move it into next sibling */
@@ -1488,6 +1560,12 @@ emoticon_insert_span (EHTMLEditorView *view,
                        WEBKIT_DOM_NODE (selection_end_marker),
                        WEBKIT_DOM_NODE (selection_start_marker),
                        NULL);
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
        }
        selection_end_marker_parent = webkit_dom_node_get_parent_node (
                WEBKIT_DOM_NODE (selection_end_marker));
@@ -1560,6 +1638,20 @@ emoticon_insert_span (EHTMLEditorView *view,
                webkit_dom_html_element_insert_adjacent_html (
                        WEBKIT_DOM_HTML_ELEMENT (span), "afterend", "&#8203;", NULL);
 
+       if (ev) {
+               WebKitDOMDocumentFragment *fragment;
+               WebKitDOMNode *node;
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+               node = webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (span), TRUE),
+                       NULL);
+               webkit_dom_html_element_insert_adjacent_html (
+                       WEBKIT_DOM_HTML_ELEMENT (node), "afterend", "&#8203;", NULL);
+               ev->data.fragment = fragment;
+       }
+
        /* Remove the text that represents the text version of smiley that was
         * written into the composer. */
        if (node_text && view->priv->smiley_written) {
@@ -1576,6 +1668,16 @@ emoticon_insert_span (EHTMLEditorView *view,
                view->priv->smiley_written = FALSE;
        }
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        e_html_editor_selection_restore (selection);
 
        e_html_editor_view_set_changed (view, TRUE);
@@ -1910,6 +2012,9 @@ body_keydown_event_cb (WebKitDOMElement *element,
        key_code = webkit_dom_ui_event_get_key_code (event);
        if (key_code == HTML_KEY_CODE_CONTROL)
                html_editor_view_set_links_active (view, TRUE);
+       else if (key_code == HTML_KEY_CODE_DELETE ||
+                key_code == HTML_KEY_CODE_BACKSPACE)
+               view->priv->dont_save_history_in_body_input = TRUE;
 }
 
 static void
@@ -1918,6 +2023,10 @@ body_keypress_event_cb (WebKitDOMElement *element,
                         EHTMLEditorView *view)
 {
        glong key_code;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
 
        view->priv->return_key_pressed = FALSE;
        view->priv->space_key_pressed = FALSE;
@@ -1927,6 +2036,102 @@ body_keypress_event_cb (WebKitDOMElement *element,
                view->priv->return_key_pressed = TRUE;
        else if (key_code == HTML_KEY_CODE_SPACE)
                view->priv->space_key_pressed = TRUE;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       if (!webkit_dom_range_get_collapsed (range, NULL)) {
+               EHTMLEditorViewHistoryEvent *ev;
+               WebKitDOMDocumentFragment *fragment;
+
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_DELETE;
+
+               fragment = webkit_dom_range_clone_contents (range, NULL);
+               ev->data.fragment = fragment;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       view->priv->selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+}
+
+static void
+save_history_for_input (EHTMLEditorView *view)
+{
+       EHTMLEditorViewHistoryEvent *ev;
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       if (!webkit_dom_dom_selection_get_range_count (dom_selection))
+               return;
+
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       ev->type = HISTORY_INPUT;
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       e_html_editor_selection_get_selection_coordinates (
+               view->priv->selection,
+               &ev->after.start.x,
+               &ev->after.start.y,
+               &ev->after.end.x,
+               &ev->after.end.y);
+       webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "character");
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       fragment = webkit_dom_range_clone_contents (range, NULL);
+       /* We have to specialy handle Return key press */
+       if (view->priv->return_key_pressed) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+               webkit_dom_node_append_child (
+                       node,
+                       WEBKIT_DOM_NODE (
+                               webkit_dom_document_create_element (document, "br", NULL)),
+                       NULL);
+               webkit_dom_node_append_child (
+                       node,
+                       WEBKIT_DOM_NODE (
+                               create_selection_marker (document, TRUE)),
+                       NULL);
+               webkit_dom_node_append_child (
+                       node,
+                       WEBKIT_DOM_NODE (
+                               create_selection_marker (document, FALSE)),
+                       NULL);
+
+               remove_node (webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)));
+       } else {
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       WEBKIT_DOM_NODE (
+                               create_selection_marker (document, TRUE)),
+                       NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       WEBKIT_DOM_NODE (
+                               create_selection_marker (document, FALSE)),
+                       NULL);
+       }
+
+       webkit_dom_dom_selection_modify (dom_selection, "move", "right", "character");
+
+       ev->data.fragment = fragment;
+       e_html_editor_view_insert_new_history_event (view, ev);
 }
 
 static void
@@ -1944,9 +2149,23 @@ body_input_event_cb (WebKitDOMElement *element,
 
        e_html_editor_view_set_changed (view, TRUE);
 
-       if (view->priv->magic_smileys)
+       if (view->priv->undo_redo_in_progress) {
+               view->priv->undo_redo_in_progress = FALSE;
+               view->priv->dont_save_history_in_body_input = FALSE;
+               e_html_editor_view_force_spell_check_for_current_paragraph (view);
+               return;
+       }
+
+       if (!view->priv->dont_save_history_in_body_input)
+               save_history_for_input (view);
+       else
+               e_html_editor_view_force_spell_check_for_current_paragraph (view);
+
+       if (view->priv->magic_smileys && !view->priv->dont_save_history_in_body_input)
                html_editor_view_check_magic_smileys (view, range);
 
+       view->priv->dont_save_history_in_body_input = FALSE;
+
        if (view->priv->return_key_pressed || view->priv->space_key_pressed) {
                html_editor_view_check_magic_links (view, range, FALSE);
                mark_node_as_paragraph_after_ending_list (selection, document);
@@ -2140,7 +2359,10 @@ body_input_event_cb (WebKitDOMElement *element,
 
                        /* Wrap and quote the line */
                        if (!remove_quoting && text_length >= word_wrap_length) {
+                               EHTMLEditorViewHistoryEvent *ev;
+
                                remove_quoting_from_element (block);
+                               remove_wrapping_from_element (block);
 
                                block = e_html_editor_selection_wrap_paragraph_length (
                                        selection, block, length);
@@ -2156,6 +2378,16 @@ body_input_event_cb (WebKitDOMElement *element,
                                                NULL,
                                                NULL);
 
+                               /* The content was wrapped and the coordinates
+                                * of caret could be changed, so renew them. */
+                               ev = view->priv->history->data;
+                               e_html_editor_selection_get_selection_coordinates (
+                                       selection,
+                                       &ev->after.start.x,
+                                       &ev->after.start.y,
+                                       &ev->after.end.x,
+                                       &ev->after.end.y);
+
                                e_html_editor_selection_restore (selection);
                                e_html_editor_view_force_spell_check_for_current_paragraph  (view);
 
@@ -2385,21 +2617,46 @@ clipboard_text_received_for_paste_as_text (GtkClipboard *clipboard,
                                            EHTMLEditorView *view)
 {
        EHTMLEditorSelection *selection;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
 
        if (!text || !*text)
                return;
 
        selection = e_html_editor_view_get_selection (view);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_PASTE_AS_TEXT;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.string.from = NULL;
+               ev->data.string.to = g_strdup (text);
+       }
+
        e_html_editor_selection_insert_as_text (selection, text);
+
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
 }
 
-static void
-clipboard_text_received (GtkClipboard *clipboard,
-                         const gchar *text,
-                         EHTMLEditorView *view)
+void
+e_html_editor_view_insert_quoted_text (EHTMLEditorView *view,
+                                       const gchar *text)
 {
        EHTMLEditorSelection *selection;
+       EHTMLEditorViewHistoryEvent *ev = NULL;
        gchar *escaped_text;
        WebKitDOMDocument *document;
        WebKitDOMElement *blockquote, *element, *selection_start;
@@ -2446,6 +2703,20 @@ clipboard_text_received (GtkClipboard *clipboard,
 
        e_html_editor_selection_save (selection);
 
+       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
+               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+               ev->type = HISTORY_PASTE_QUOTED;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.string.from = NULL;
+               ev->data.string.to = g_strdup (text);
+       }
+
        selection_start = webkit_dom_document_get_element_by_id (
                document, "-x-evo-selection-start-marker");
        sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start));
@@ -2476,12 +2747,32 @@ clipboard_text_received (GtkClipboard *clipboard,
 
        e_html_editor_selection_restore_caret_position (selection);
 
+       if (ev) {
+               e_html_editor_selection_get_selection_coordinates (
+                       selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
        e_html_editor_view_force_spell_check_for_current_paragraph (view);
 
+       e_html_editor_view_set_changed (view, TRUE);
+
        g_free (escaped_text);
 }
 
 static void
+clipboard_text_received (GtkClipboard *clipboard,
+                         const gchar *text,
+                         EHTMLEditorView *view)
+{
+       e_html_editor_view_insert_quoted_text (view, text);
+}
+
+static void
 html_editor_view_set_property (GObject *object,
                                guint property_id,
                                const GValue *value,
@@ -2555,14 +2846,14 @@ html_editor_view_get_property (GObject *object,
 
                case PROP_CAN_REDO:
                        g_value_set_boolean (
-                               value, webkit_web_view_can_redo (
-                               WEBKIT_WEB_VIEW (object)));
+                               value, e_html_editor_view_can_redo (
+                               E_HTML_EDITOR_VIEW (object)));
                        return;
 
                case PROP_CAN_UNDO:
                        g_value_set_boolean (
-                               value, webkit_web_view_can_undo (
-                               WEBKIT_WEB_VIEW (object)));
+                               value, e_html_editor_view_can_undo (
+                               E_HTML_EDITOR_VIEW (object)));
                        return;
 
                case PROP_CHANGED:
@@ -2612,6 +2903,57 @@ html_editor_view_get_property (GObject *object,
 }
 
 static void
+free_history_event_content (EHTMLEditorViewHistoryEvent *event)
+{
+       switch (event->type) {
+               case HISTORY_INPUT:
+               case HISTORY_DELETE:
+               case HISTORY_CITATION_SPLIT:
+               case HISTORY_IMAGE:
+               case HISTORY_SMILEY:
+               case HISTORY_REMOVE_LINK:
+                       if (event->data.fragment != NULL)
+                               g_object_unref (event->data.fragment);
+                       break;
+               case HISTORY_FONT_COLOR:
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       if (event->data.string.from != NULL)
+                               g_free (event->data.string.from);
+                       if (event->data.string.to != NULL)
+                               g_free (event->data.string.to);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+               case HISTORY_IMAGE_DIALOG:
+               case HISTORY_CELL_DIALOG:
+               case HISTORY_TABLE_DIALOG:
+               case HISTORY_PAGE_DIALOG:
+                       if (event->data.dom.from != NULL)
+                               g_object_unref (event->data.dom.from);
+                       if (event->data.dom.to != NULL)
+                               g_object_unref (event->data.dom.to);
+                       break;
+               default:
+                       break;
+       }
+}
+
+static void
+free_history_event (EHTMLEditorViewHistoryEvent *event)
+{
+       if (event == NULL)
+               return;
+
+       free_history_event_content (event);
+
+       g_free (event);
+}
+
+static void
 html_editor_view_dispose (GObject *object)
 {
        EHTMLEditorViewPrivate *priv;
@@ -2638,6 +2980,11 @@ html_editor_view_dispose (GObject *object)
                priv->mail_settings = NULL;
        }
 
+       if (priv->history != NULL) {
+               g_list_free_full (priv->history, (GDestroyNotify) free_history_event);
+               priv->history = NULL;
+       }
+
        g_hash_table_remove_all (priv->inline_images);
 
        /* Chain up to parent's dispose() method. */
@@ -3110,6 +3457,155 @@ fix_structure_after_delete_before_quoted_content (EHTMLEditorView *view)
        return FALSE;
 }
 
+static void
+save_history_for_delete_or_backspace (EHTMLEditorView *view,
+                                      gboolean delete_key)
+{
+       EHTMLEditorSelection *selection;
+       EHTMLEditorViewHistoryEvent *ev;
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       if (!webkit_dom_dom_selection_get_range_count (dom_selection))
+               return;
+
+       selection = e_html_editor_view_get_selection (view);
+
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       ev->type = HISTORY_DELETE;
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       e_html_editor_selection_get_selection_coordinates (
+               selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       if (webkit_dom_range_get_collapsed (range, NULL)) {
+               WebKitDOMNode *node = webkit_dom_range_get_end_container (range, NULL);
+
+               if (delete_key && !webkit_dom_node_get_next_sibling (node)) {
+                       g_free (ev);
+                       return;
+               }
+
+               if (delete_key)
+                       webkit_dom_dom_selection_modify (dom_selection, "extend", "right", "character");
+               else
+                       webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "character");
+
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               fragment = webkit_dom_range_clone_contents (range, NULL);
+
+               if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment))) {
+                       g_free (ev);
+                       return;
+               }
+
+               if (delete_key) {
+                       webkit_dom_dom_selection_collapse_to_start (dom_selection, NULL);
+
+                       e_html_editor_selection_get_selection_coordinates (
+                               selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
+               } else {
+                       e_html_editor_selection_get_selection_coordinates (
+                               selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
+
+                       webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
+               }
+
+               if (!delete_key) {
+                       ev->after.end.x = ev->after.start.x;
+                       ev->after.end.y = ev->after.start.y;
+               }
+
+               if (delete_key) {
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (
+                                       create_selection_marker (document, FALSE)),
+                               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)),
+                               NULL);
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (
+                                       create_selection_marker (document, TRUE)),
+                               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)),
+                               NULL);
+               } else {
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (
+                                       create_selection_marker (document, TRUE)),
+                               NULL);
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (
+                                       create_selection_marker (document, FALSE)),
+                               NULL);
+               }
+       } else {
+               ev->after.start.x = ev->before.start.x;
+               ev->after.start.y = ev->before.start.y;
+               ev->after.end.x = ev->before.end.x;
+               ev->after.end.y = ev->before.end.y;
+               fragment = webkit_dom_range_clone_contents (range, NULL);
+       }
+
+       ev->data.fragment = fragment;
+       e_html_editor_view_insert_new_history_event (view, ev);
+}
+
+static gboolean
+split_citation (EHTMLEditorView *view)
+{
+       EHTMLEditorSelection *selection;
+       EHTMLEditorViewHistoryEvent *ev;
+       WebKitDOMElement *element;
+
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       ev->type = HISTORY_CITATION_SPLIT;
+
+       selection = e_html_editor_view_get_selection (view);
+       e_html_editor_selection_get_selection_coordinates (
+               selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+
+       if (!e_html_editor_selection_is_collapsed (selection)) {
+               WebKitDOMDocument *document;
+               WebKitDOMDocumentFragment *fragment;
+               WebKitDOMDOMWindow *dom_window;
+               WebKitDOMDOMSelection *dom_selection;
+               WebKitDOMRange *range;
+
+               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+               if (!webkit_dom_dom_selection_get_range_count (dom_selection))
+                       return FALSE;
+
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               fragment = webkit_dom_range_clone_contents (range, NULL);
+
+               ev->data.fragment = fragment;
+       } else
+               ev->data.fragment = NULL;
+
+       element = insert_new_line_into_citation (view, "");
+
+       e_html_editor_selection_get_selection_coordinates (
+               selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
+
+       e_html_editor_view_insert_new_history_event (view, ev);
+
+       return element != NULL;
+}
+
 static gboolean
 selection_is_in_table (WebKitDOMDocument *document,
                        gboolean *first_cell,
@@ -3307,11 +3803,71 @@ delete_character_from_quoted_line_start (EHTMLEditorView *view)
 }
 
 static gboolean
+insert_tabulator (EHTMLEditorView *view)
+{
+       gboolean success;
+       EHTMLEditorViewHistoryEvent *ev;
+
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       ev->type = HISTORY_INPUT;
+
+       e_html_editor_selection_get_selection_coordinates (
+               view->priv->selection,
+               &ev->before.start.x,
+               &ev->before.start.y,
+               &ev->before.end.x,
+               &ev->before.end.y);
+
+       success = e_html_editor_view_exec_command (
+               view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, "\t");
+
+       if (success) {
+               WebKitDOMDocument *document;
+               WebKitDOMElement *element;
+               WebKitDOMDocumentFragment *fragment;
+
+               e_html_editor_selection_get_selection_coordinates (
+                       view->priv->selection,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+               fragment = webkit_dom_document_create_document_fragment (document);
+               element = webkit_dom_document_create_element (document, "span", NULL);
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (element), "\t", NULL);
+               webkit_dom_element_set_attribute (
+                       element, "class", "Apple-tab-span", NULL);
+               webkit_dom_element_set_attribute (
+                       element, "style", "white-space:pre", NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment), WEBKIT_DOM_NODE (element), NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       WEBKIT_DOM_NODE (create_selection_marker (document, TRUE)),
+                       NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       WEBKIT_DOM_NODE (create_selection_marker (document, FALSE)),
+                       NULL);
+               ev->data.fragment = fragment;
+
+               e_html_editor_view_insert_new_history_event (view, ev);
+       }
+
+       return success;
+}
+
+static gboolean
 html_editor_view_key_press_event (GtkWidget *widget,
                                   GdkEventKey *event)
 {
        EHTMLEditorView *view = E_HTML_EDITOR_VIEW (widget);
 
+       view->priv->dont_save_history_in_body_input = FALSE;
+
        if (event->keyval == GDK_KEY_Menu) {
                gboolean event_handled, collapsed;
                EHTMLEditorSelection *selection;
@@ -3334,8 +3890,7 @@ html_editor_view_key_press_event (GtkWidget *widget,
                        return TRUE;
 
                if (event->keyval == GDK_KEY_Tab)
-                       return e_html_editor_view_exec_command (
-                               view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, "\t");
+                       return insert_tabulator (view);
                else
                        return FALSE;
        }
@@ -3382,7 +3937,7 @@ html_editor_view_key_press_event (GtkWidget *widget,
                 * the special command to do it. */
                if (e_html_editor_selection_is_citation (selection)) {
                        remove_input_event_listener_from_body (view);
-                       return (insert_new_line_into_citation (view, "")) ? TRUE : FALSE;
+                       return split_citation (view);
                }
 
                /* When the return is pressed in a H1-6 element, WebKit doesn't
@@ -3444,6 +3999,7 @@ html_editor_view_key_press_event (GtkWidget *widget,
        }
 
        if (event->keyval == GDK_KEY_Delete || event->keyval == GDK_KEY_BackSpace) {
+               save_history_for_delete_or_backspace (view, event->keyval == GDK_KEY_Delete);
                if (event->keyval == GDK_KEY_BackSpace && !view->priv->html_mode) {
                        if (delete_character_from_quoted_line_start (view))
                                return TRUE;
@@ -5987,6 +6543,8 @@ e_html_editor_view_exec_command (EHTMLEditorView *view,
                CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_USE_CSS, "UseCSS", TRUE)
        }
 
+       view->priv->dont_save_history_in_body_input = 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 : "" );
@@ -7862,6 +8420,30 @@ wrap_paragraphs_in_quoted_content (EHTMLEditorSelection *selection,
        g_object_unref (paragraphs);
 }
 
+static void
+remove_whole_event_history (EHTMLEditorView *view)
+{
+       EHTMLEditorViewHistoryEvent *ev;
+
+       if (view->priv->history != NULL) {
+               g_list_free_full (view->priv->history, (GDestroyNotify) free_history_event);
+               view->priv->history = NULL;
+       }
+
+       view->priv->history_size = 0;
+       view->priv->dont_save_history_in_body_input = FALSE;
+       view->priv->undo_redo_in_progress = FALSE;
+
+       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
+       ev->type = HISTORY_START;
+       view->priv->history = g_list_append (view->priv->history, ev);
+
+       view->priv->can_undo = FALSE;
+       g_object_notify (G_OBJECT (view), "can-undo");
+       view->priv->can_redo = FALSE;
+       g_object_notify (G_OBJECT (view), "can-redo");
+}
+
 /**
  * e_html_editor_view_set_html_mode:
  * @view: an #EHTMLEditorView
@@ -7968,6 +8550,8 @@ e_html_editor_view_set_html_mode (EHTMLEditorView *view,
        }
 
  out:
+       remove_whole_event_history (view);
+
        g_object_notify (G_OBJECT (view), "html-mode");
 }
 
@@ -8111,6 +8695,9 @@ e_html_editor_view_init (EHTMLEditorView *view)
                (GDestroyNotify) g_free,
                (GDestroyNotify) g_free);
 
+       view->priv->history = NULL;
+       remove_whole_event_history (view);
+
        e_html_editor_view_update_fonts (view);
 
        /* Give spell check languages to WebKit */
@@ -8134,6 +8721,8 @@ e_html_editor_view_init (EHTMLEditorView *view)
        view->priv->return_key_pressed = FALSE;
        view->priv->space_key_pressed = FALSE;
        view->priv->smiley_written = FALSE;
+       view->priv->undo_redo_in_progress = FALSE;
+       view->priv->dont_save_history_in_body_input = FALSE;
 
        g_object_set (
                G_OBJECT (settings),
@@ -9268,3 +9857,1567 @@ e_html_editor_view_set_remove_initial_input_line (EHTMLEditorView *view,
 
        view->priv->remove_initial_input_line = value;
 }
+
+gboolean
+e_html_editor_view_is_undo_redo_in_progress (EHTMLEditorView *view)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), FALSE);
+
+       return view->priv->undo_redo_in_progress;
+}
+
+void
+e_html_editor_view_set_undo_redo_in_progress (EHTMLEditorView *view,
+                                              gboolean value)
+{
+       view->priv->undo_redo_in_progress = value;
+}
+
+#if d(1)+0
+static void
+print_fragment_inner_html (WebKitDOMDocumentFragment *fragment)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *div;
+       gchar *inner_html;
+
+       if (!fragment) {
+               printf ("\tNone'\n");
+               return;
+       }
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (fragment));
+       div = webkit_dom_document_create_element (document, "div", NULL);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (div),
+               webkit_dom_node_clone_node (WEBKIT_DOM_NODE (fragment), TRUE),
+               NULL);
+
+       inner_html = webkit_dom_html_element_get_inner_html (WEBKIT_DOM_HTML_ELEMENT (div));
+       printf ("\t'%s'\n", inner_html);
+       remove_node (WEBKIT_DOM_NODE (div));
+       g_free (inner_html);
+}
+
+static void
+print_node_inner_html (WebKitDOMNode *fragment)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *div;
+       gchar *inner_html;
+
+       if (!fragment) {
+               printf ("\tnone\n");
+               return;
+       }
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (fragment));
+       div = webkit_dom_document_create_element (document, "div", NULL);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (div),
+               webkit_dom_node_clone_node (WEBKIT_DOM_NODE (fragment), TRUE),
+               NULL);
+
+       inner_html = webkit_dom_html_element_get_inner_html (WEBKIT_DOM_HTML_ELEMENT (div));
+       remove_node (WEBKIT_DOM_NODE (div));
+
+       printf ("\t'%s'\n", inner_html);
+
+       g_free (inner_html);
+}
+
+static void
+print_history_event (EHTMLEditorViewHistoryEvent *event)
+{
+       printf ("HISTORY EVENT: %d ; \n", event->type);
+       printf ("\t before: start_x: %u ; start_y: %u ; end_x: %u ; end_y: %u ;\n", event->before.start.x, 
event->before.start.y, event->before.end.x, event->before.end.y);
+       printf ("\t after:  start_x: %u ; start_y: %u ; end_x: %u ; end_y: %u ;\n", event->after.start.x, 
event->after.start.y, event->after.end.x, event->after.end.y);
+       switch (event->type) {
+               case HISTORY_DELETE:
+               case HISTORY_INPUT:
+               case HISTORY_REMOVE_LINK:
+               case HISTORY_SMILEY:
+               case HISTORY_IMAGE:
+               case HISTORY_CITATION_SPLIT:
+                       print_fragment_inner_html (event->data.fragment);
+                       break;
+               case HISTORY_ALIGNMENT:
+               case HISTORY_BLOCK_FORMAT:
+               case HISTORY_BOLD:
+               case HISTORY_FONT_SIZE:
+               case HISTORY_INDENT:
+               case HISTORY_ITALIC:
+               case HISTORY_MONOSPACE:
+               case HISTORY_UNDERLINE:
+               case HISTORY_STRIKETHROUGH:
+               case HISTORY_WRAP:
+                       printf (" from %d to %d ;\n", event->data.style.from, event->data.style.to);
+                       break;
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+                       printf (" pasting: '%s' ; \n", event->data.string.to);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+               case HISTORY_IMAGE_DIALOG:
+               case HISTORY_CELL_DIALOG:
+               case HISTORY_TABLE_DIALOG:
+               case HISTORY_PAGE_DIALOG:
+                       print_node_inner_html (event->data.dom.from);
+                       print_node_inner_html (event->data.dom.to);
+                       break;
+               case HISTORY_FONT_COLOR:
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       printf (" from '%s' to '%s';\n", event->data.string.from, event->data.string.to);
+                       break;
+               case HISTORY_START:
+                       printf ("HISTORY START\n");
+                       break;
+               default:
+                       printf ("Unknown history type\n");
+       }
+}
+
+static void
+print_history (EHTMLEditorView *view)
+{
+       if (view->priv->history) {
+               printf ("\n");
+               g_list_foreach (
+                       view->priv->history,
+                       (GFunc) print_history_event,
+                       NULL);
+               printf ("\n");
+       } else {
+               printf ("History empty!\n");
+       }
+}
+
+static void
+print_undo_events (EHTMLEditorView *view)
+{
+       GList *item = view->priv->history;
+
+       printf ("UNDO EVENTS:\n");
+       if (!item || !item->next) {
+               printf ("EMPTY\n");
+               return;
+       }
+
+       print_history_event (item->data);
+       item = item->next;
+       while (item) {
+               print_history_event (item->data);
+               item = item->next;
+       }
+
+       printf ("\n");
+
+}
+
+static void
+print_redo_events (EHTMLEditorView *view)
+{
+       GList *item = view->priv->history;
+
+       printf ("REDO EVENTS:\n");
+       if (!item || !item->prev) {
+               printf ("EMPTY\n");
+               return;
+       }
+
+       item = item->prev;
+       while (item) {
+               print_history_event (item->data);
+               item = item->prev;
+       }
+
+       printf ("\n");
+
+}
+#endif
+
+static void
+remove_history_event (EHTMLEditorView *view,
+                     GList *item)
+{
+       free_history_event_content (item->data);
+
+       view->priv->history = g_list_delete_link (view->priv->history, item);
+       view->priv->history_size--;
+}
+
+static void
+remove_forward_redo_history_events_if_needed (EHTMLEditorView *view)
+{
+       GList *history = view->priv->history;
+       GList *item;
+
+       if (!history || !history->prev)
+               return;
+
+       item = history->prev;
+       while (item) {
+               GList *prev_item = item->prev;
+
+               remove_history_event (view, item);
+               item = prev_item;
+       }
+}
+
+void
+e_html_editor_view_insert_new_history_event (EHTMLEditorView *view,
+                                            EHTMLEditorViewHistoryEvent *event)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+       if (view->priv->undo_redo_in_progress)
+               return;
+
+       d (print_history_event (event));
+
+       remove_forward_redo_history_events_if_needed (view);
+
+       if (view->priv->history_size >= HISTORY_SIZE_LIMIT)
+               remove_history_event (view, g_list_last (view->priv->history)->prev);
+
+       view->priv->history = g_list_prepend (view->priv->history, event);
+       view->priv->history_size++;
+       view->priv->can_undo = TRUE;
+
+       g_object_notify (G_OBJECT (view), "can-undo");
+}
+
+static WebKitDOMRange *
+get_range_for_point (WebKitDOMDocument *document,
+                    EHTMLEditorViewSelectionPoint point)
+{
+       glong scroll_left, scroll_top;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMRange *range;
+
+       body = webkit_dom_document_get_body (document);
+       scroll_left = webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body));
+       scroll_top = webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body));
+
+       range = webkit_dom_document_caret_range_from_point (
+               document, point.x - scroll_left, point.y - scroll_top);
+
+       /* The point is outside the viewport, scroll to it. */
+       if (!range) {
+               WebKitDOMDOMWindow *window;
+
+               window = webkit_dom_document_get_default_view (document);
+               webkit_dom_dom_window_scroll_to (window, point.x, point.y);
+
+               scroll_left = webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body));
+               scroll_top = webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body));
+               range = webkit_dom_document_caret_range_from_point (
+                       document, point.x - scroll_left, point.y - scroll_top);
+       }
+
+       return range;
+}
+
+static void
+restore_selection_to_history_event_state (EHTMLEditorView *view,
+                                         EHTMLEditorViewSelection selection_state)
+{
+       EHTMLEditorSelection *selection;
+       gboolean was_collapsed = FALSE;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMElement *element, *tmp;
+       WebKitDOMRange *range;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       /* Restore the selection how it was before the event occured. */
+       selection = e_html_editor_view_get_selection (view);
+       range = get_range_for_point (document, selection_state.start);
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+       was_collapsed = selection_state.start.x == selection_state.end.x;
+       was_collapsed = was_collapsed &&  selection_state.start.y == selection_state.end.y;
+       if (was_collapsed)
+               return;
+
+       e_html_editor_selection_save (selection);
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+
+       remove_node (WEBKIT_DOM_NODE (element));
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       webkit_dom_element_remove_attribute (element, "id");
+
+       range = get_range_for_point (document, selection_state.end);
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+       e_html_editor_selection_save (selection);
+
+       tmp = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       remove_node (WEBKIT_DOM_NODE (tmp));
+
+       webkit_dom_element_set_id (
+               element, "-x-evo-selection-start-marker");
+
+       e_html_editor_selection_restore (selection);
+}
+
+static void
+undo_delete (EHTMLEditorView *view,
+            EHTMLEditorViewHistoryEvent *event)
+{
+       gboolean empty, single_block;
+       gchar *content;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+       WebKitDOMElement *element;
+       WebKitDOMNode *first_child, *fragment;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       fragment = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (event->data.fragment),  TRUE);
+       first_child = webkit_dom_node_get_first_child (fragment);
+
+       content = webkit_dom_node_get_text_content (fragment);
+       empty = content && !*content;
+       g_free (content);
+
+       /* Tabulator */
+       single_block = event->type == HISTORY_INPUT;
+       single_block = single_block && event->before.start.x != 0 && event->before.end.y != 0;
+
+       if (!single_block) {
+               /* One block delete */
+               if ((single_block = WEBKIT_DOM_IS_ELEMENT (first_child)))
+                       single_block = element_has_id (WEBKIT_DOM_ELEMENT (first_child), 
"-x-evo-selection-start-marker");
+               else
+                       single_block = WEBKIT_DOM_IS_TEXT (first_child);
+       }
+
+       /* Redoing Return key press */
+       if (empty) {
+               EHTMLEditorSelection *selection;
+               WebKitDOMNode *node;
+
+               selection = e_html_editor_view_get_selection (view);
+
+               range = get_range_for_point (document, event->before.start);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+
+               node = webkit_dom_range_get_start_container (range, NULL);
+               if (!node)
+                       return;
+
+               element = get_parent_block_element (node);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       fragment,
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
+                       NULL);
+               e_html_editor_selection_restore (selection);
+               e_html_editor_view_force_spell_check (view);
+
+               return;
+       }
+
+       /* Multi block delete */
+       if (WEBKIT_DOM_IS_ELEMENT (first_child) && !single_block) {
+               EHTMLEditorSelection *selection;
+               WebKitDOMNode *node, *parent, *last_child;
+               WebKitDOMNode *parent_deleted_content;
+               WebKitDOMNode *parent_current_block;
+               WebKitDOMNode *insert_before;
+
+               selection = e_html_editor_view_get_selection (view);
+
+               range = get_range_for_point (document, event->before.start);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+               e_html_editor_selection_save (selection);
+
+               element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+
+               /* Get the last block in deleted content. */
+               last_child = webkit_dom_node_get_last_child (fragment);
+               while (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child))
+                       last_child = webkit_dom_node_get_last_child (last_child);
+
+               /* All the nodes that are in current block after the caret position
+                * belongs on the end of the deleted content. */
+               node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+               while (node) {
+                       WebKitDOMNode *next_sibling;
+
+                       next_sibling = webkit_dom_node_get_next_sibling (node);
+                       webkit_dom_node_append_child (last_child, node, NULL);
+                       node = next_sibling;
+               }
+
+               /* Get the first block in deleted content. */
+               while (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (first_child))
+                       first_child = webkit_dom_node_get_first_child (first_child);
+
+               /* All the nodes that are in the first block of the deleted content
+                * belongs to the current block right after the caret position. */
+               parent = get_parent_block_node_from_child (WEBKIT_DOM_NODE (element));
+               while ((node = webkit_dom_node_get_first_child (first_child)))
+                       webkit_dom_node_append_child (WEBKIT_DOM_NODE (parent), node, NULL);
+
+               parent_deleted_content = webkit_dom_node_get_parent_node (first_child);
+               parent_current_block = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent));
+               insert_before = webkit_dom_node_get_next_sibling (parent);
+
+               /* Remove the first block from deleted content as its content was already
+                * moved to the right place. */
+               remove_node (first_child);
+
+               /* Move the deleted content back to the body. Start from the next sibling
+                * of the first block (if presented) where the delete occured. */
+               while (parent_deleted_content) {
+                       WebKitDOMNode *tmp, *sibling;
+
+                       /* Move all the siblings from current level back to the body. */
+                       sibling = webkit_dom_node_get_first_child (parent_deleted_content);
+                       while (sibling) {
+                               WebKitDOMNode *next_sibling;
+
+                               next_sibling = webkit_dom_node_get_next_sibling (sibling);
+                               webkit_dom_node_insert_before (
+                                       parent_current_block, sibling, insert_before, NULL);
+                               sibling = next_sibling;
+                       }
+                       tmp = webkit_dom_node_get_parent_node (parent_deleted_content);
+                       remove_node (parent_deleted_content);
+                       parent_deleted_content = tmp;
+                       insert_before = webkit_dom_node_get_next_sibling (parent_current_block);
+                       parent_current_block = webkit_dom_node_get_parent_node (parent_current_block);
+               }
+
+               e_html_editor_selection_restore (selection);
+               e_html_editor_view_force_spell_check (view);
+       } else {
+               WebKitDOMNode *inserted_node, *nd;
+
+               element = webkit_dom_document_create_element (document, "span", NULL);
+
+               range = get_range_for_point (document, event->after.start);
+               /* Create temporary node on the selection where the delete occured. */
+               webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element), NULL);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+
+               nd = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+               if (nd && WEBKIT_DOM_IS_TEXT (nd)) {
+                       gchar *text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (nd));
+                       glong length = webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (nd));
+
+                       /* 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 (nd), 0, 1, "", NULL);
+                               else if (g_str_has_suffix (text, UNICODE_ZERO_WIDTH_SPACE))
+                                       webkit_dom_character_data_replace_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (nd), length - 1, 1, "", NULL);
+                       }
+                       g_free (text);
+               }
+
+               /* Insert the deleted content back to the body. */
+               inserted_node = webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       fragment,
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+
+               remove_node (WEBKIT_DOM_NODE (element));
+
+               /* If the selection markers are presented restore the selection,
+                * otherwise the selection was not callapsed so select the deleted
+                * content as it was before the delete occured. */
+               if (webkit_dom_document_fragment_query_selector (event->data.fragment, 
"span#-x-evo-selection-start-marker", NULL)) {
+                       e_html_editor_selection_restore (e_html_editor_view_get_selection (view));
+                       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               } else {
+                       webkit_dom_range_select_node (range, WEBKIT_DOM_NODE (inserted_node), NULL);
+                       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                       webkit_dom_dom_selection_add_range (dom_selection, range);
+               }
+
+               if (view->priv->magic_smileys)
+                       html_editor_view_check_magic_smileys (view, range);
+               if (view->priv->magic_links)
+                       html_editor_view_check_magic_links (view, range, FALSE);
+               e_html_editor_view_force_spell_check_for_current_paragraph (view);
+       }
+}
+
+static void
+redo_delete (EHTMLEditorView *view,
+            EHTMLEditorViewHistoryEvent *event)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMDocumentFragment *fragment = event->data.fragment;
+       WebKitDOMNode *first_child;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+
+       restore_selection_to_history_event_state (view, event->before);
+
+       if (webkit_dom_document_fragment_query_selector (fragment, "span#-x-evo-selection-start-marker", 
NULL)) {
+               gboolean delete = FALSE;
+
+               /* Check if the event was delete or backspace press. */
+               delete = WEBKIT_DOM_IS_ELEMENT (first_child);
+               delete = delete && element_has_id (WEBKIT_DOM_ELEMENT (first_child), 
"-x-evo-selection-start-marker");
+               if (delete)
+                       webkit_dom_dom_selection_modify (dom_selection, "extend", "right", "character");
+               else
+                       webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "character");
+       }
+
+       e_html_editor_view_exec_command (view, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
+
+       e_html_editor_view_force_spell_check_for_current_paragraph (view);
+}
+
+typedef void (*SelectionStyleChangeFunc) (EHTMLEditorSelection *selection, gint style);
+
+static void
+undo_redo_style_change (EHTMLEditorView *view,
+                       EHTMLEditorViewHistoryEvent *event,
+                       gboolean undo)
+{
+       EHTMLEditorSelection *selection;
+       SelectionStyleChangeFunc func;
+
+       selection = e_html_editor_view_get_selection (view);
+
+       switch (event->type) {
+               case HISTORY_ALIGNMENT:
+                       func = (SelectionStyleChangeFunc) e_html_editor_selection_set_alignment;
+                       break;
+               case HISTORY_BOLD:
+                       func = e_html_editor_selection_set_bold;
+                       break;
+               case HISTORY_BLOCK_FORMAT:
+                       func = (SelectionStyleChangeFunc) e_html_editor_selection_set_block_format;
+                       break;
+               case HISTORY_FONT_SIZE:
+                       func = (SelectionStyleChangeFunc) e_html_editor_selection_set_font_size;
+                       break;
+               case HISTORY_ITALIC:
+                       func = e_html_editor_selection_set_italic;
+                       break;
+               case HISTORY_MONOSPACE:
+                       func = e_html_editor_selection_set_monospaced;
+                       break;
+               case HISTORY_STRIKETHROUGH:
+                       func = e_html_editor_selection_set_strikethrough;
+                       break;
+               case HISTORY_UNDERLINE:
+                       func = e_html_editor_selection_set_underline;
+                       break;
+               default:
+                       return;
+       }
+
+       restore_selection_to_history_event_state (view, undo ? event->after : event->before);
+
+       func (selection, undo ? event->data.style.from : event->data.style.to);
+
+       restore_selection_to_history_event_state (view, undo ? event->before : event->after);
+}
+
+static void
+undo_redo_indent (EHTMLEditorView *view,
+                 EHTMLEditorViewHistoryEvent *event,
+                 gboolean undo)
+{
+       gboolean was_indent = FALSE;
+       EHTMLEditorSelection *selection;
+
+       selection = e_html_editor_view_get_selection (view);
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->after);
+
+       was_indent = event->data.style.from && event->data.style.to;
+
+       if (undo) {
+               if (was_indent)
+                       e_html_editor_selection_unindent (selection);
+               else
+                       e_html_editor_selection_indent (selection);
+       } else {
+               if (was_indent)
+                       e_html_editor_selection_indent (selection);
+               else
+                       e_html_editor_selection_unindent (selection);
+       }
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->before);
+}
+
+static void
+undo_redo_font_color (EHTMLEditorView *view,
+                     EHTMLEditorViewHistoryEvent *event,
+                     gboolean undo)
+{
+       if (undo)
+               restore_selection_to_history_event_state (view, event->after);
+
+       e_html_editor_view_exec_command (
+               view,
+               E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR,
+               undo ? event->data.string.from : event->data.string.to);
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->before);
+}
+
+static void
+undo_redo_wrap (EHTMLEditorView *view,
+               EHTMLEditorViewHistoryEvent *event,
+               gboolean undo)
+{
+       EHTMLEditorSelection *selection;
+
+       selection = e_html_editor_view_get_selection (view);
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->after);
+
+       if (undo) {
+               WebKitDOMNode *node;
+               WebKitDOMElement *element;
+               WebKitDOMRange *range;
+
+               range = html_editor_view_get_dom_range (view);
+               node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+               element = get_parent_block_element (WEBKIT_DOM_NODE (node));
+               remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (element));
+
+               e_html_editor_view_force_spell_check_for_current_paragraph (view);
+       } else
+               e_html_editor_selection_wrap_lines (selection);
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->before);
+}
+
+static void
+undo_redo_page_dialog (EHTMLEditorView *view,
+                      EHTMLEditorViewHistoryEvent *event,
+                      gboolean undo)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNamedNodeMap *attributes, *attributes_history;
+       gint length, length_history, ii, jj;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       body = webkit_dom_document_get_body (document);
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->after);
+
+       if (undo) {
+               attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+               attributes_history = webkit_dom_element_get_attributes (
+                       WEBKIT_DOM_ELEMENT (event->data.dom.from));
+       } else {
+               attributes_history = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+               attributes = webkit_dom_element_get_attributes (
+                       WEBKIT_DOM_ELEMENT (event->data.dom.to));
+       }
+
+       length = webkit_dom_named_node_map_get_length (attributes);
+       length_history = webkit_dom_named_node_map_get_length (attributes_history);
+       for (ii = length - 1; ii >= 0; ii--) {
+               gchar *name;
+               WebKitDOMNode *attr;
+               gboolean replaced = FALSE;
+
+               attr = webkit_dom_named_node_map_item (attributes, ii);
+               name = webkit_dom_node_get_local_name (attr);
+
+               for (jj = length_history - 1; jj >= 0; jj--) {
+                       gchar *name_history;
+                       WebKitDOMNode *attr_history;
+
+                       attr_history = webkit_dom_named_node_map_item (attributes_history, jj);
+                       name_history = webkit_dom_node_get_local_name (attr_history);
+                       if (g_strcmp0 (name, name_history) == 0) {
+                               WebKitDOMNode *attr_clone;
+
+                               attr_clone = webkit_dom_node_clone_node (
+                                               undo ? attr_history : attr, TRUE);
+                               webkit_dom_element_set_attribute_node (
+                                       WEBKIT_DOM_ELEMENT (body),
+                                       WEBKIT_DOM_ATTR (attr_clone),
+                                       NULL);
+
+                               /* Link color has to replaced in HEAD as well. */
+                               if (g_strcmp0 (name, "link") == 0) {
+                                       gchar *value;
+                                       GdkRGBA rgba;
+
+                                       value = webkit_dom_node_get_node_value (attr_clone);
+                                       if (gdk_rgba_parse (&rgba, value))
+                                               e_html_editor_view_set_link_color (view, &rgba);
+                                       g_free (value);
+                               }
+                               replaced = TRUE;
+                       }
+                       g_free (name_history);
+                       g_object_unref (attr_history);
+                       if (replaced)
+                               break;
+               }
+
+               if (!replaced) {
+                       if (undo) {
+                               webkit_dom_element_remove_attribute_node (
+                                       WEBKIT_DOM_ELEMENT (body),
+                                       WEBKIT_DOM_ATTR (attr),
+                                       NULL);
+                       } else {
+                               webkit_dom_element_set_attribute_node (
+                                       WEBKIT_DOM_ELEMENT (body),
+                                       WEBKIT_DOM_ATTR (
+                                               webkit_dom_node_clone_node (attr, TRUE)),
+                                       NULL);
+                       }
+               }
+               g_free (name);
+               g_object_unref (attr);
+       }
+       g_object_unref (attributes);
+       g_object_unref (attributes_history);
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->before);
+}
+
+static void
+undo_redo_hrule_dialog (EHTMLEditorView *view,
+                        EHTMLEditorViewHistoryEvent *event,
+                        gboolean undo)
+{
+       EHTMLEditorSelection *selection;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+
+       selection = e_html_editor_view_get_selection (view);
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->after);
+
+       e_html_editor_selection_save (selection);
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       if (undo) {
+               WebKitDOMNode *node;
+               WebKitDOMElement *parent;
+
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (element));
+               if (event->data.dom.from)
+                       node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (parent));
+               else
+                       node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent));
+
+               if (node && WEBKIT_DOM_IS_HTMLHR_ELEMENT (node)) {
+                       if (!event->data.dom.from)
+                               remove_node (node);
+                       else
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (node),
+                                       event->data.dom.from,
+                                       node,
+                                       NULL);
+               }
+       } else {
+               WebKitDOMNode *node;
+               WebKitDOMElement *parent;
+
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (element));
+
+               if (event->data.dom.from) {
+                       node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent));
+
+                       if (node && WEBKIT_DOM_IS_HTMLHR_ELEMENT (node))
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (node),
+                                       event->data.dom.to,
+                                       node,
+                                       NULL);
+               } else {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)),
+                               event->data.dom.to,
+                               webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)),
+                               NULL);
+               }
+       }
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->before);
+}
+
+static void
+undo_redo_image_dialog (EHTMLEditorView *view,
+                        EHTMLEditorViewHistoryEvent *event,
+                        gboolean undo)
+{
+       EHTMLEditorSelection *selection;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+       WebKitDOMNode *sibling, *image = NULL;
+
+       selection = e_html_editor_view_get_selection (view);
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->after);
+
+       e_html_editor_selection_save (selection);
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+       if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling)) {
+               if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (sibling))
+                       image = sibling;
+               else if (element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-resizable-wrapper"))
+                       image = webkit_dom_node_get_first_child (sibling);
+       }
+
+       if (!image) {
+               element = WEBKIT_DOM_ELEMENT (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)));
+               sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+               if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling)) {
+                       if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (sibling))
+                               image = sibling;
+                       else if (element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-resizable-wrapper"))
+                               image = webkit_dom_node_get_first_child (sibling);
+               }
+       }
+
+       if (!image)
+               return;
+
+       webkit_dom_node_replace_child (
+               webkit_dom_node_get_parent_node (image),
+               undo ? event->data.dom.from : event->data.dom.to,
+               image,
+               NULL);
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->before);
+}
+
+static void
+undo_redo_table_dialog (EHTMLEditorView *view,
+                        EHTMLEditorViewHistoryEvent *event,
+                        gboolean undo)
+{
+       EHTMLEditorSelection *selection;
+
+       WebKitDOMDocument *document;
+       WebKitDOMElement *table, *element;
+
+       selection = e_html_editor_view_get_selection (view);
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->after);
+
+       e_html_editor_selection_save (selection);
+       element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker");
+       if (!element)
+               return;
+
+       table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (element), "TABLE");
+
+       if (!table) {
+               if ((!event->data.dom.to && undo) || (!event->data.dom.from && !undo)) {
+                       WebKitDOMElement *parent;
+
+                       parent = get_parent_block_element (WEBKIT_DOM_NODE (element));
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)),
+                               undo ? event->data.dom.from : event->data.dom.to,
+                               WEBKIT_DOM_NODE (parent),
+                               NULL);
+                       restore_selection_to_history_event_state (view, event->before);
+                       return;
+               } else
+                       return;
+       }
+
+       if (undo) {
+               if (!event->data.dom.from)
+                       remove_node (WEBKIT_DOM_NODE (table));
+               else
+                       webkit_dom_node_replace_child (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)),
+                               event->data.dom.from,
+                               WEBKIT_DOM_NODE (table),
+                               NULL);
+       } else {
+               if (!event->data.dom.to)
+                       remove_node (WEBKIT_DOM_NODE (table));
+               else
+                       webkit_dom_node_replace_child (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)),
+                               event->data.dom.to,
+                               WEBKIT_DOM_NODE (table),
+                               NULL);
+       }
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->before);
+}
+
+static void
+undo_redo_paste (EHTMLEditorView *view,
+                 EHTMLEditorViewHistoryEvent *event,
+                 gboolean undo)
+{
+       EHTMLEditorSelection *selection;
+       WebKitDOMDocument *document;
+
+       selection = e_html_editor_view_get_selection (view);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+       if (undo) {
+               if (event->type == HISTORY_PASTE_QUOTED) {
+                       WebKitDOMElement *tmp;
+                       WebKitDOMNode *parent;
+                       WebKitDOMNode *sibling;
+
+                       restore_selection_to_history_event_state (view, event->after);
+
+                       e_html_editor_selection_save (selection);
+                       tmp = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+                       if (!tmp)
+                               return;
+
+                       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp));
+                       while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (parent)))
+                               parent = webkit_dom_node_get_parent_node (parent);
+
+                       sibling = webkit_dom_node_get_previous_sibling (parent);
+                       if (sibling) {
+                               add_selection_markers_into_element_end (document, WEBKIT_DOM_ELEMENT 
(sibling), NULL, NULL);
+
+                               remove_node (parent);
+                       } else {
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (parent),
+                                       WEBKIT_DOM_NODE (prepare_paragraph (selection, document, TRUE)),
+                                       parent,
+                                       NULL);
+                       }
+                       e_html_editor_selection_restore (selection);
+               } else {
+                       WebKitDOMDOMWindow *dom_window;
+                       WebKitDOMDOMSelection *dom_selection;
+                       WebKitDOMElement *element, *tmp;
+                       WebKitDOMRange *range;
+
+                       dom_window = webkit_dom_document_get_default_view (document);
+                       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+                       /* Restore the selection how it was before the event occured. */
+                       range = get_range_for_point (document, event->before.start);
+                       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+                       e_html_editor_selection_save (selection);
+
+                       element = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-end-marker");
+
+                       remove_node (WEBKIT_DOM_NODE (element));
+
+                       element = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+
+                       webkit_dom_element_remove_attribute (element, "id");
+
+                       range = get_range_for_point (document, event->after.start);
+                       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+                       e_html_editor_selection_save (selection);
+
+                       tmp = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+
+                       remove_node (WEBKIT_DOM_NODE (tmp));
+
+                       webkit_dom_element_set_id (
+                               element, "-x-evo-selection-start-marker");
+
+                       e_html_editor_selection_restore (selection);
+
+                       e_html_editor_view_exec_command (
+                               view, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
+
+                       e_html_editor_view_force_spell_check_for_current_paragraph (view);
+               }
+       } else {
+               restore_selection_to_history_event_state (view, event->before);
+
+               if (event->type == HISTORY_PASTE)
+                       e_html_editor_selection_insert_text (selection, event->data.string.to);
+               else if (event->type == HISTORY_PASTE_QUOTED)
+                       e_html_editor_view_insert_quoted_text (view, event->data.string.to);
+               else if (event->type == HISTORY_INSERT_HTML)
+                       e_html_editor_selection_insert_html (selection, event->data.string.to);
+               else
+                       e_html_editor_selection_insert_as_text (selection, event->data.string.to);
+       }
+}
+
+static void
+undo_redo_image (EHTMLEditorView *view,
+                 EHTMLEditorViewHistoryEvent *event,
+                 gboolean undo)
+{
+       EHTMLEditorSelection *selection;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+
+       selection = e_html_editor_view_get_selection (view);
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       if (undo) {
+               WebKitDOMElement *element;
+               WebKitDOMNode *node;
+
+               range = get_range_for_point (document, event->before.start);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+
+               e_html_editor_selection_save (selection);
+               element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+
+               node = webkit_dom_node_get_next_sibling  (WEBKIT_DOM_NODE (element));
+
+               if (WEBKIT_DOM_IS_ELEMENT (node))
+                       if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-resizable-wrapper") ||
+                           element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-smiley-wrapper"))
+                               remove_node (node);
+               e_html_editor_selection_restore (selection);
+       } else {
+               WebKitDOMElement *element;
+
+               range = get_range_for_point (document, event->before.start);
+               /* Create temporary node on the selection where the delete occured. */
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+
+               e_html_editor_selection_save (selection);
+               element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+
+               /* Insert the deleted content back to the body. */
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (event->data.fragment), TRUE),
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+
+               e_html_editor_selection_restore (selection);
+               e_html_editor_view_force_spell_check_for_current_paragraph (view);
+       }
+}
+
+static void
+undo_redo_replace (EHTMLEditorView *view,
+                   EHTMLEditorViewHistoryEvent *event,
+                   gboolean undo)
+{
+       restore_selection_to_history_event_state (view, undo ? event->after : event->before);
+
+       if (undo) {
+               WebKitDOMDocument *document;
+               WebKitDOMDOMWindow *dom_window;
+               WebKitDOMDOMSelection *dom_selection;
+
+               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+               webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "word");
+       }
+
+       e_html_editor_view_exec_command (
+               view,
+               E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT,
+               undo ? event->data.string.from : event->data.string.to);
+
+       e_html_editor_view_force_spell_check_for_current_paragraph (view);
+
+       restore_selection_to_history_event_state (view, undo ? event->before : event->after);
+}
+
+static void
+undo_redo_replace_all (EHTMLEditorView *view,
+                       EHTMLEditorViewHistoryEvent *event,
+                       gboolean undo)
+{
+       if (undo) {
+               if (event->type == HISTORY_REPLACE) {
+                       undo_redo_replace (view, event, undo);
+                       return;
+               } else {
+                       EHTMLEditorViewHistoryEvent *next_event;
+                       GList *next_item;
+                       WebKitDOMDocument *document;
+                       WebKitDOMDOMWindow *dom_window;
+                       WebKitDOMDOMSelection *dom_selection;
+
+                       next_item = view->priv->history->next;
+
+                       while (next_item) {
+                               next_event = next_item->data;
+
+                               if (next_event->type != HISTORY_REPLACE)
+                                       break;
+
+                               if (g_strcmp0 (next_event->data.string.from, event->data.string.from) != 0)
+                                       break;
+
+                               if (g_strcmp0 (next_event->data.string.to, event->data.string.to) != 0)
+                                       break;
+
+                               undo_redo_replace (view, next_event, undo);
+
+                               next_item = next_item->next;
+                       }
+
+                       view->priv->history = next_item->prev;
+
+                       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+                       dom_window = webkit_dom_document_get_default_view (document);
+                       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+                       webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
+               }
+       } else {
+               /* Find if this history item is part of HISTORY_REPLACE_ALL. */
+               EHTMLEditorViewHistoryEvent *prev_event;
+               GList *prev_item;
+               gboolean replace_all = FALSE;
+
+               prev_item = view->priv->history->prev;
+               while (prev_item) {
+                       prev_event = prev_item->data;
+
+                       if (prev_event->type == HISTORY_REPLACE)
+                               prev_item = prev_item->prev;
+                       else if (prev_event->type == HISTORY_REPLACE_ALL) {
+                               replace_all = TRUE;
+                               break;
+                       } else
+                               break;
+               }
+
+               if (!replace_all) {
+                       undo_redo_replace (view, event, undo);
+                       return;
+               }
+
+               prev_item = view->priv->history->prev;
+               while (prev_item) {
+                       prev_event = prev_item->data;
+
+                       if (prev_event->type == HISTORY_REPLACE) {
+                               undo_redo_replace (view, prev_event, undo);
+                               prev_item = prev_item->prev;
+                       } else
+                               break;
+               }
+
+               view->priv->history = prev_item->next;
+       }
+}
+
+static void
+undo_redo_remove_link (EHTMLEditorView *view,
+                       EHTMLEditorViewHistoryEvent *event,
+                       gboolean undo)
+{
+       EHTMLEditorSelection *selection;
+
+       selection = e_html_editor_view_get_selection (view);
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->after);
+
+       if (undo) {
+               WebKitDOMDocument *document;
+               WebKitDOMDOMWindow *dom_window;
+               WebKitDOMDOMSelection *dom_selection;
+               WebKitDOMElement *element;
+               WebKitDOMRange *range;
+
+
+               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+               /* Select the anchor. */
+               webkit_dom_dom_selection_modify (dom_selection, "move", "left", "word");
+               webkit_dom_dom_selection_modify (dom_selection, "extend", "right", "word");
+
+               range = html_editor_view_get_dom_range (view);
+               element = webkit_dom_document_create_element (document, "SPAN", NULL);
+               webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element), NULL);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (event->data.fragment), TRUE),
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+               remove_node (WEBKIT_DOM_NODE (element));
+       } else
+               e_html_editor_selection_unlink (selection);
+
+       if (undo)
+               restore_selection_to_history_event_state (view, event->before);
+}
+
+static void
+undo_input (EHTMLEditorView *view,
+            EHTMLEditorViewHistoryEvent *event)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       restore_selection_to_history_event_state (view, event->after);
+
+       webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "character");
+       if (e_html_editor_selection_is_citation (view->priv->selection)) {
+               /* Post processing of quoted text in body_input_event_cb needs to be called. */
+               view->priv->undo_redo_in_progress = FALSE;
+               view->priv->dont_save_history_in_body_input = TRUE;
+       }
+       e_html_editor_view_exec_command (
+               view, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
+}
+
+static void
+undo_redo_citation_split (EHTMLEditorView *view,
+                          EHTMLEditorViewHistoryEvent *event,
+                          gboolean undo)
+{
+       if (undo) {
+               EHTMLEditorSelection *selection;
+               WebKitDOMDocument *document;
+               WebKitDOMElement *selection_start, *parent;
+               WebKitDOMNode *citation_before, *citation_after, *child, *last_child, *tmp;
+               gint citation_level = 1, length, word_wrap_length;
+
+               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+
+               restore_selection_to_history_event_state (view, event->after);
+
+               selection = e_html_editor_view_get_selection (view);
+               e_html_editor_selection_save (selection);
+               selection_start = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               if (!selection_start)
+                       return;
+
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start));
+
+               citation_before = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (parent));
+               if (!is_citation_node (citation_before))
+                       return;
+
+               citation_after = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent));
+               if (!is_citation_node (citation_after))
+                       return;
+
+               /* Get first block in next citation. */
+               child = webkit_dom_node_get_first_child (citation_after);
+               while (child && is_citation_node (child)) {
+                       citation_level++;
+                       child = webkit_dom_node_get_first_child (child);
+               }
+
+               remove_quoting_from_element (WEBKIT_DOM_ELEMENT (child));
+               remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (child));
+
+               /* Get last block in previous citation. */
+               last_child = webkit_dom_node_get_last_child (citation_before);
+               while (last_child && is_citation_node (last_child))
+                       last_child = webkit_dom_node_get_last_child (last_child);
+
+               remove_quoting_from_element (WEBKIT_DOM_ELEMENT (last_child));
+               remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (last_child));
+
+               /* Copy the content of the first block to the last block to get
+                * to the state how the block looked like before it was split. */
+               while ((tmp = webkit_dom_node_get_first_child (child)))
+                       webkit_dom_node_append_child (last_child, tmp, NULL);
+
+               word_wrap_length = e_html_editor_selection_get_word_wrap_length (selection);
+               length = word_wrap_length - 2 * citation_level;
+
+               /* We need to re-wrap and re-quote the block. */
+               last_child = WEBKIT_DOM_NODE (e_html_editor_selection_wrap_paragraph_length (
+                       view->priv->selection, WEBKIT_DOM_ELEMENT (last_child), length));
+               quote_plain_text_element_after_wrapping (
+                       document, WEBKIT_DOM_ELEMENT (last_child), citation_level);
+
+               remove_node (child);
+
+               /* Move all the block from next citation to the previous one. */
+               while ((child = webkit_dom_node_get_first_child (citation_after)))
+                       webkit_dom_node_append_child (citation_before, child, NULL);
+
+               remove_node (WEBKIT_DOM_NODE (parent));
+               remove_node (WEBKIT_DOM_NODE (citation_after));
+
+               /* If enter was pressed when some text was selected, restore it. */
+               if (event->data.fragment != NULL)
+                       undo_delete (view, event);
+
+               restore_selection_to_history_event_state (view, event->before);
+
+               e_html_editor_view_force_spell_check (view);
+       } else {
+               insert_new_line_into_citation (view, "");
+       }
+}
+
+void
+e_html_editor_view_redo (EHTMLEditorView *view)
+{
+       EHTMLEditorViewHistoryEvent *event;
+       GList *history;
+
+       history = view->priv->history;
+       if (!history || !history->prev)
+               return;
+
+       event = history->prev->data;
+       d (print_history_event (event));
+
+       view->priv->undo_redo_in_progress = TRUE;
+
+       switch (event->type) {
+               case HISTORY_BOLD:
+               case HISTORY_MONOSPACE:
+               case HISTORY_STRIKETHROUGH:
+               case HISTORY_UNDERLINE:
+               case HISTORY_ALIGNMENT:
+               case HISTORY_BLOCK_FORMAT:
+               case HISTORY_FONT_SIZE:
+               case HISTORY_ITALIC:
+                       undo_redo_style_change (view, event, FALSE);
+                       break;
+               case HISTORY_DELETE:
+                       redo_delete (view, event);
+                       break;
+               case HISTORY_INDENT:
+                       undo_redo_indent (view, event, FALSE);
+                       break;
+               case HISTORY_INPUT:
+                       undo_delete (view, event);
+                       break;
+               case HISTORY_REMOVE_LINK:
+                       undo_redo_remove_link (view, event, FALSE);
+                       break;
+               case HISTORY_FONT_COLOR:
+                       undo_redo_font_color (view, event, FALSE);
+                       break;
+               case HISTORY_CITATION_SPLIT:
+                       undo_redo_citation_split (view, event, FALSE);
+                       break;
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+                       undo_redo_paste (view, event, FALSE);
+                       break;
+               case HISTORY_IMAGE:
+               case HISTORY_SMILEY:
+                       undo_redo_image (view, event, FALSE);
+                       break;
+               case HISTORY_WRAP:
+                       undo_redo_wrap (view, event, FALSE);
+                       break;
+               case HISTORY_IMAGE_DIALOG:
+                       undo_redo_image_dialog (view, event, FALSE);
+                       break;
+               case HISTORY_TABLE_DIALOG:
+                       undo_redo_table_dialog (view, event, FALSE);
+                       break;
+               case HISTORY_PAGE_DIALOG:
+                       undo_redo_page_dialog (view, event, FALSE);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+                       undo_redo_hrule_dialog (view, event, FALSE);
+                       break;
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       undo_redo_replace_all (view, event, FALSE);
+                       break;
+               default:
+                       return;
+       }
+
+       view->priv->history = view->priv->history->prev;
+
+       d (print_history (view));
+
+       html_editor_view_user_changed_contents_cb (view);
+
+       view->priv->undo_redo_in_progress = FALSE;
+}
+
+static gboolean
+event_selection_was_collapsed (EHTMLEditorViewHistoryEvent *ev)
+{
+       return (ev->before.start.x == ev->before.end.x) && (ev->before.start.y == ev->before.end.y);
+}
+
+void
+e_html_editor_view_undo (EHTMLEditorView *view)
+{
+       EHTMLEditorViewHistoryEvent *event;
+       GList *history;
+
+       history = view->priv->history;
+       if (!history)
+               return;
+
+       event = history->data;
+       d (print_history_event (event));
+
+       view->priv->undo_redo_in_progress = TRUE;
+
+       switch (event->type) {
+               case HISTORY_BOLD:
+               case HISTORY_ITALIC:
+               case HISTORY_STRIKETHROUGH:
+               case HISTORY_UNDERLINE:
+               case HISTORY_FONT_SIZE:
+                       if (event_selection_was_collapsed (event)) {
+                               if (history->next) {
+                                       view->priv->history = history->next;
+                                       e_html_editor_view_undo (view);
+                               }
+                               view->priv->undo_redo_in_progress = FALSE;
+                               return;
+                       }
+               case HISTORY_ALIGNMENT:
+               case HISTORY_BLOCK_FORMAT:
+               case HISTORY_MONOSPACE:
+                       undo_redo_style_change (view, event, TRUE);
+                       break;
+               case HISTORY_DELETE:
+                       undo_delete (view, event);
+                       break;
+               case HISTORY_INDENT:
+                       undo_redo_indent (view, event, TRUE);
+                       break;
+               case HISTORY_INPUT:
+                       undo_input (view, event);
+                       break;
+               case HISTORY_REMOVE_LINK:
+                       undo_redo_remove_link (view, event, TRUE);
+                       break;
+               case HISTORY_FONT_COLOR:
+                       undo_redo_font_color (view, event, TRUE);
+                       break;
+               case HISTORY_CITATION_SPLIT:
+                       undo_redo_citation_split (view, event, TRUE);
+                       break;
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+                       undo_redo_paste (view, event, TRUE);
+                       break;
+               case HISTORY_IMAGE:
+               case HISTORY_SMILEY:
+                       undo_redo_image (view, event, TRUE);
+                       break;
+               case HISTORY_WRAP:
+                       undo_redo_wrap (view, event, TRUE);
+                       break;
+               case HISTORY_IMAGE_DIALOG:
+                       undo_redo_image_dialog (view, event, TRUE);
+                       break;
+               case HISTORY_TABLE_DIALOG:
+                       undo_redo_table_dialog (view, event, TRUE);
+                       break;
+               case HISTORY_PAGE_DIALOG:
+                       undo_redo_page_dialog (view, event, TRUE);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+                       undo_redo_hrule_dialog (view, event, TRUE);
+                       break;
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       undo_redo_replace_all (view, event, TRUE);
+                       break;
+               default:
+                       return;
+       }
+
+       if (history->next)
+               view->priv->history = view->priv->history->next;
+
+       d (print_history (view));
+
+       html_editor_view_user_changed_contents_cb (view);
+
+       view->priv->undo_redo_in_progress = FALSE;
+}
diff --git a/e-util/e-html-editor-view.h b/e-util/e-html-editor-view.h
index 77dd60c..a61fd08 100644
--- a/e-util/e-html-editor-view.h
+++ b/e-util/e-html-editor-view.h
@@ -75,6 +75,76 @@ struct _EHTMLEditorViewClass {
                                                (EHTMLEditorView *view);
 };
 
+enum EHTMLEditorViewHistoryEventType {
+       HISTORY_ALIGNMENT,
+       HISTORY_BLOCK_FORMAT,
+       HISTORY_BOLD,
+       HISTORY_CELL_DIALOG,
+       HISTORY_DELETE, /* BackSpace, Delete, with and without selection */
+       HISTORY_FONT_COLOR,
+       HISTORY_FONT_SIZE,
+       HISTORY_HRULE_DIALOG,
+       HISTORY_INDENT,
+       HISTORY_INPUT,
+       HISTORY_IMAGE,
+       HISTORY_IMAGE_DIALOG,
+       HISTORY_INSERT_HTML,
+       HISTORY_ITALIC,
+       HISTORY_MONOSPACE,
+       HISTORY_PAGE_DIALOG,
+       HISTORY_PASTE,
+       HISTORY_PASTE_AS_TEXT,
+       HISTORY_PASTE_QUOTED,
+       HISTORY_REMOVE_LINK,
+       HISTORY_REPLACE,
+       HISTORY_REPLACE_ALL,
+       HISTORY_CITATION_SPLIT,
+       HISTORY_SMILEY,
+       HISTORY_START, /* Start of history */
+       HISTORY_STRIKETHROUGH,
+       HISTORY_TABLE_DIALOG,
+       HISTORY_UNDERLINE,
+       HISTORY_WRAP
+};
+
+typedef struct {
+       gint from; /* From what format we are changing. */
+       gint to; /* To what format we are changing. */
+} EHTMLEditorViewStyleChange;
+
+/* This is used for e-html-editor-*-dialogs */
+typedef struct {
+       WebKitDOMNode *from; /* From what node we are changing. */
+       WebKitDOMNode *to; /* To what node we are changing. */
+} EHTMLEditorViewDOMChange;
+
+typedef struct {
+       gchar *from; /* From what format we are changing. */
+       gchar *to; /* To what format we are changing. */
+} EHTMLEditorViewStringChange;
+
+typedef struct {
+       guint x;
+       guint y;
+} EHTMLEditorViewSelectionPoint;
+
+typedef struct {
+       EHTMLEditorViewSelectionPoint start;
+       EHTMLEditorViewSelectionPoint end;
+} EHTMLEditorViewSelection;
+
+typedef struct {
+       enum EHTMLEditorViewHistoryEventType type;
+       EHTMLEditorViewSelection before;
+       EHTMLEditorViewSelection after;
+       union {
+               WebKitDOMDocumentFragment *fragment;
+               EHTMLEditorViewStyleChange style;
+               EHTMLEditorViewStringChange string;
+               EHTMLEditorViewDOMChange dom;
+       } data;
+} EHTMLEditorViewHistoryEvent;
+
 GType          e_html_editor_view_get_type     (void) G_GNUC_CONST;
 EHTMLEditorView *
                e_html_editor_view_new          (void);
@@ -184,11 +254,26 @@ void              e_html_editor_view_set_is_message_from_edit_as_new
 void           e_html_editor_view_set_remove_initial_input_line
                                                (EHTMLEditorView *view,
                                                 gboolean value);
+void           e_html_editor_view_insert_quoted_text
+                                               (EHTMLEditorView *view,
+                                                const gchar *text);
 WebKitDOMElement *
                get_parent_block_element        (WebKitDOMNode *node);
 void           e_html_editor_view_set_link_color
                                                (EHTMLEditorView *view,
                                                 GdkRGBA *color);
+gboolean       e_html_editor_view_can_undo     (EHTMLEditorView *view);
+void           e_html_editor_view_undo         (EHTMLEditorView *view);
+gboolean       e_html_editor_view_can_redo     (EHTMLEditorView *view);
+void           e_html_editor_view_redo         (EHTMLEditorView *view);
+void           e_html_editor_view_insert_new_history_event
+                                               (EHTMLEditorView *view,
+                                                EHTMLEditorViewHistoryEvent *event);
+gboolean       e_html_editor_view_is_undo_redo_in_progress
+                                               (EHTMLEditorView *view);
+void           e_html_editor_view_set_undo_redo_in_progress
+                                               (EHTMLEditorView *view,
+                                                gboolean value);
 G_END_DECLS
 
 #endif /* E_HTML_EDITOR_VIEW_H */



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