[evolution] Bug 724651 - [webkit-composer] Undo or Redo does not work
- From: Tomas Popela <tpopela src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] Bug 724651 - [webkit-composer] Undo or Redo does not work
- Date: Thu, 12 Mar 2015 13:49:10 +0000 (UTC)
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", "​", 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", "​", 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", "​", 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]