[evolution/webkit-composer: 10/130] Initial import of GtkhtmlEditor class



commit 3f88de651b38e612df5a911fd7975a10736bb4fb
Author: Dan VrÃtil <dvratil redhat com>
Date:   Thu Jul 26 21:18:08 2012 +0200

    Initial import of GtkhtmlEditor class
    
    GtkhtmlEditor => EEditor
    
    EEditor is a GtkBox which handles installation of menu actions and toolbars and
    bindings between EEditorWidget and these actions.
    
    As of now, only elementary formatting works.

 e-util/Makefile.am        |    6 +
 e-util/e-editor-actions.c | 2438 +++++++++++++++++++++++++++++++++++++++++++++
 e-util/e-editor-actions.h |  152 +++
 e-util/e-editor-private.h |   72 ++
 e-util/e-editor-widgets.h |  234 +++++
 e-util/e-editor.c         |  576 +++++++++++
 e-util/e-editor.h         |   95 ++
 e-util/e-util.h           |    3 +
 8 files changed, 3576 insertions(+), 0 deletions(-)
---
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 4e0659d..c5452ff 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -171,8 +171,11 @@ eutilinclude_HEADERS =  \
 	e-destination-store.h \
 	e-dialog-utils.h \
 	e-dialog-widgets.h \
+	e-editor-actions.h \
 	e-editor-selection.h \
 	e-editor-widget.h \
+	e-editor-widgets.h \
+	e-editor.h \
 	e-emoticon-action.h \
 	e-emoticon-chooser-menu.h \
 	e-emoticon-chooser.h \
@@ -426,8 +429,11 @@ libeutil_la_SOURCES = \
 	e-destination-store.c \
 	e-dialog-utils.c \
 	e-dialog-widgets.c \
+	e-editor-actions.c \
+	e-editor-private.h \
 	e-editor-selection.c \
 	e-editor-widget.c \
+	e-editor.c \
 	e-emoticon-action.c \
 	e-emoticon-chooser-menu.c \
 	e-emoticon-chooser.c \
diff --git a/e-util/e-editor-actions.c b/e-util/e-editor-actions.c
new file mode 100644
index 0000000..74f89f3
--- /dev/null
+++ b/e-util/e-editor-actions.c
@@ -0,0 +1,2438 @@
+/* e-editor-actions.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+//#include "e-editor-private.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+
+#include "e-editor.h"
+#include "e-editor-actions.h"
+#include "e-editor-private.h"
+#include "e-editor-widgets.h"
+#include "e-emoticon-action.h"
+#include "e-image-chooser-dialog.h"
+
+static void
+insert_html_file_ready_cb (GFile *file,
+                           GAsyncResult *result,
+                           EEditor *editor)
+{
+	EEditorSelection *selection;
+	gchar *contents = NULL;
+	gsize length;
+	GError *error = NULL;
+
+	g_file_load_contents_finish (
+		file, result, &contents, &length, NULL, &error);
+	if (error != NULL) {
+		GtkWidget *dialog;
+
+		dialog = gtk_message_dialog_new (
+			GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))),
+			0, GTK_MESSAGE_ERROR,
+			GTK_BUTTONS_CLOSE, _("Failed to insert HTML file."));
+		gtk_message_dialog_format_secondary_text (
+			GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
+		gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+
+		g_clear_error (&error);
+		g_object_unref (editor);
+		return;
+	}
+
+	selection = e_editor_widget_get_selection (
+			e_editor_get_editor_widget (editor));
+	e_editor_selection_insert_html (selection, contents);
+	g_free (contents);
+
+	g_object_unref (editor);
+}
+
+static void
+insert_text_file_ready_cb (GFile *file,
+                           GAsyncResult *result,
+                           EEditor *editor)
+{
+	EEditorSelection *selection;
+	gchar *contents;
+	gsize length;
+	GError *error = NULL;
+
+	g_file_load_contents_finish (
+		file, result, &contents, &length, NULL, &error);
+	if (error != NULL) {
+		GtkWidget *dialog;
+
+		dialog = gtk_message_dialog_new (
+			GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))),
+			0, GTK_MESSAGE_ERROR,
+			GTK_BUTTONS_CLOSE, _("Failed to insert text file."));
+		gtk_message_dialog_format_secondary_text (
+			GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
+		gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+
+		g_clear_error (&error);
+		g_object_unref (editor);
+		return;
+	}
+
+	selection = e_editor_widget_get_selection (
+			e_editor_get_editor_widget (editor));
+	e_editor_selection_insert_text (selection, contents);
+	g_free (contents);
+
+	g_object_unref (editor);
+}
+
+static void
+replace_answer (EEditor *editor,
+                EEditorWidgetReplaceAnswer answer)
+{
+	/* FIXME WEBKIT
+	if (e_editor_widget_replace (e_editor_get_editor_widget (editor), answer))
+		gtk_widget_hide (WIDGET (REPLACE_CONFIRMATION_WINDOW));
+	*/
+}
+
+/*****************************************************************************
+ * Action Callbacks
+ *****************************************************************************/
+
+static void
+action_bold_cb (GtkToggleAction *action,
+                EEditor *editor)
+{
+	EEditorSelection *selection;
+
+	selection = e_editor_widget_get_selection (
+			e_editor_get_editor_widget (editor));
+	e_editor_selection_set_bold (
+		selection, gtk_toggle_action_get_active (action));
+}
+
+
+static void
+action_confirm_replace_cb (GtkAction *action,
+                           EEditor *editor)
+{
+	replace_answer (editor, E_EDITOR_WIDGET_REPLACE_ANSWER_REPLACE);
+}
+
+static void
+action_confirm_replace_all_cb (GtkAction *action,
+                               EEditor *editor)
+{
+	replace_answer (editor, E_EDITOR_WIDGET_REPLACE_ANSWER_REPLACE_ALL);
+}
+
+static void
+action_confirm_replace_cancel_cb (GtkAction *action,
+                                  EEditor *editor)
+{
+	replace_answer (editor, E_EDITOR_WIDGET_REPLACE_ANSWER_CANCEL);
+}
+
+static void
+action_confirm_replace_next_cb (GtkAction *action,
+                                EEditor *editor)
+{
+	replace_answer (editor, E_EDITOR_WIDGET_REPLACE_ANSWER_NEXT);
+}
+
+static WebKitDOMNode *
+find_parent_element_by_type (WebKitDOMNode *node, GType type)
+{
+	while (node) {
+
+		if (g_type_is_a (type, webkit_dom_node_get_type ()))
+			return node;
+
+		node = webkit_dom_node_get_parent_node (node);
+	}
+
+	return NULL;
+}
+
+static void
+action_context_delete_cell_cb (GtkAction *action,
+                               EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	WebKitDOMNode *start, *end, *cell;
+	gboolean single_row;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+		return;
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+
+	/* Find TD in which the selection starts */
+	start = webkit_dom_range_get_start_container (range, NULL);
+	if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (start)) {
+		start = find_parent_element_by_type (
+			start, WEBKIT_TYPE_DOM_HTML_TABLE_CELL_ELEMENT);
+	}
+
+	/* Find TD in which the selection ends */
+	end = webkit_dom_range_get_end_container (range, NULL);
+	if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (end)) {
+		end = find_parent_element_by_type (
+			end, WEBKIT_TYPE_DOM_HTML_TABLE_CELL_ELEMENT);
+	}
+
+	single_row = (webkit_dom_node_get_parent_node (start) ==
+			webkit_dom_node_get_parent_node (end));
+
+	cell = start;
+	while (cell) {
+		WebKitDOMNodeList *nodes;
+		gulong length, i;
+
+		/* Remove all child nodes in the cell */
+		nodes = webkit_dom_node_get_child_nodes (cell);
+		length = webkit_dom_node_list_get_length (nodes);
+		for (i = 0; i < length; i++) {
+			webkit_dom_node_remove_child (
+				cell,
+				webkit_dom_node_list_item (nodes, i),
+				NULL);
+		}
+
+		if (cell == end)
+			break;
+
+		cell = webkit_dom_node_get_next_sibling (cell);
+
+		if (!cell && !single_row) {
+			cell = webkit_dom_node_get_first_child (
+				webkit_dom_node_get_parent_node (cell));
+		}
+	}
+}
+
+static void
+action_context_delete_column_cb (GtkAction *action,
+                                 EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	WebKitDOMNode *start, *end, *first_row;
+	gulong index, count, i;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1) {
+		return;
+	}
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+
+	/* Find TD in which the selection starts */
+	start = webkit_dom_range_get_start_container (range, NULL);
+	if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (start)) {
+		start = find_parent_element_by_type (
+			start, WEBKIT_TYPE_DOM_HTML_TABLE_CELL_ELEMENT);
+	}
+
+	/* Find TD in which the selection ends */
+	end = webkit_dom_range_get_end_container (range, NULL);
+	if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (end)) {
+		end = find_parent_element_by_type (
+			end, WEBKIT_TYPE_DOM_HTML_TABLE_CELL_ELEMENT);
+	}
+
+	first_row = find_parent_element_by_type (
+		start, WEBKIT_TYPE_DOM_HTML_TABLE_ROW_ELEMENT);
+	first_row = webkit_dom_node_get_first_child (
+		webkit_dom_node_get_parent_node (first_row));
+
+	index = webkit_dom_html_table_cell_element_get_cell_index (
+			WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (start));
+	count = webkit_dom_html_table_cell_element_get_cell_index (
+			WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (end)) - index;
+
+	for (i = 0; i < count; i++) {
+		WebKitDOMNode *row = first_row;
+
+		while (row) {
+			webkit_dom_html_table_row_element_delete_cell (
+				WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row),
+				index, NULL);
+
+			row = webkit_dom_node_get_next_sibling (row);
+		}
+	}
+}
+
+static void
+action_context_delete_row_cb (GtkAction *action,
+                              EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	WebKitDOMNode *start, *end, *table;
+	gulong index, row_count, i;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+		return;
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+
+	start = webkit_dom_range_get_start_container (range, NULL);
+	if (!WEBKIT_DOM_IS_HTML_TABLE_ROW_ELEMENT (start)) {
+		start = find_parent_element_by_type (
+			start, WEBKIT_TYPE_DOM_HTML_TABLE_ROW_ELEMENT);
+	}
+
+	end = webkit_dom_range_get_end_container (range, NULL);
+	if (!WEBKIT_DOM_IS_HTML_TABLE_ROW_ELEMENT (end)) {
+		end = find_parent_element_by_type (
+			end, WEBKIT_TYPE_DOM_HTML_TABLE_ROW_ELEMENT);
+	}
+
+	table = find_parent_element_by_type (
+			start, WEBKIT_TYPE_DOM_HTML_TABLE_ELEMENT);
+
+	index = webkit_dom_html_table_row_element_get_row_index (
+			WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (start));
+	row_count = webkit_dom_html_table_row_element_get_row_index (
+			WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (end)) - index;
+
+	for (i = 0; i < row_count; i++) {
+		webkit_dom_html_table_element_delete_row (
+			WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index, NULL);
+	}
+}
+
+static void
+action_context_delete_table_cb (GtkAction *action,
+    EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	WebKitDOMNode *table;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+		return;
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+
+	table = webkit_dom_range_get_start_container (range, NULL);
+	if (!WEBKIT_DOM_IS_HTML_TABLE_ELEMENT (table)) {
+		table = find_parent_element_by_type (
+			table, WEBKIT_TYPE_DOM_HTML_TABLE_ELEMENT);
+	}
+
+	webkit_dom_node_remove_child (
+		webkit_dom_node_get_parent_node (table), table, NULL);
+}
+
+static void
+action_context_insert_column_after_cb (GtkAction *action,
+                                       EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	WebKitDOMNode *cell, *row;
+	gulong index;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+		return;
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+
+	cell = webkit_dom_range_get_end_container (range, NULL);
+	if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (cell)) {
+		cell = find_parent_element_by_type (
+			cell, WEBKIT_TYPE_DOM_HTML_TABLE_CELL_ELEMENT);
+	}
+
+	row = find_parent_element_by_type (
+		cell, WEBKIT_TYPE_DOM_HTML_TABLE_ROW_ELEMENT);
+	/* Get the first row in the table */
+	row = webkit_dom_node_get_first_child (
+		webkit_dom_node_get_parent_node (row));
+
+	index = webkit_dom_html_table_cell_element_get_cell_index (
+			WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+
+	while (row) {
+		webkit_dom_html_table_row_element_insert_cell (
+			WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index + 1, NULL);
+
+		row = webkit_dom_node_get_next_sibling (row);
+	}
+}
+
+static void
+action_context_insert_column_before_cb (GtkAction *action,
+                                        EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	WebKitDOMNode *cell, *row;
+	gulong index;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+		return;
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+
+	cell = webkit_dom_range_get_start_container (range, NULL);
+	if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (cell)) {
+		cell = find_parent_element_by_type (
+			cell, WEBKIT_TYPE_DOM_HTML_TABLE_CELL_ELEMENT);
+	}
+
+	row = find_parent_element_by_type (
+		cell, WEBKIT_TYPE_DOM_HTML_TABLE_ROW_ELEMENT);
+	/* Get the first row in the table */
+	row = webkit_dom_node_get_first_child (
+		webkit_dom_node_get_parent_node (row));
+
+	index = webkit_dom_html_table_cell_element_get_cell_index (
+			WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+
+	while (row) {
+		webkit_dom_html_table_row_element_insert_cell (
+			WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index - 1, NULL);
+
+		row = webkit_dom_node_get_next_sibling (row);
+	}
+}
+
+static void
+action_context_insert_row_above_cb (GtkAction *action,
+                                    EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	WebKitDOMNode *row, *table;
+	gulong index;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+		return;
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+
+	row = webkit_dom_range_get_start_container (range, NULL);
+	if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (row)) {
+		row = find_parent_element_by_type (
+			row, WEBKIT_TYPE_DOM_HTML_TABLE_ROW_ELEMENT);
+	}
+
+	table = find_parent_element_by_type (
+		row, WEBKIT_TYPE_DOM_HTML_TABLE_ELEMENT);
+
+	index = webkit_dom_html_table_row_element_get_row_index (
+			WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+
+	webkit_dom_html_table_element_insert_row (
+		WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index - 1, NULL);
+}
+
+static void
+action_context_insert_row_below_cb (GtkAction *action,
+                                    EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	WebKitDOMNode *row, *table;
+	gulong index;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+		return;
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+
+	row = webkit_dom_range_get_end_container (range, NULL);
+	if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (row)) {
+		row = find_parent_element_by_type (
+			row, WEBKIT_TYPE_DOM_HTML_TABLE_ROW_ELEMENT);
+	}
+
+	table = find_parent_element_by_type (
+		row, WEBKIT_TYPE_DOM_HTML_TABLE_ELEMENT);
+
+	index = webkit_dom_html_table_row_element_get_row_index (
+			WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+
+	webkit_dom_html_table_element_insert_row (
+		WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index + 1, NULL);
+}
+
+static void
+action_context_remove_link_cb (GtkAction *action,
+                               EEditor *editor)
+{
+	WebKitDOMDocument *document;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	webkit_dom_document_exec_command (document, "unlink", FALSE, "");
+}
+
+static void
+action_context_spell_add_cb (GtkAction *action,
+                             EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	WebKitSpellChecker *spell_checker;
+	gchar *word;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+		return;
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+	word = webkit_dom_range_get_text (range);
+
+	spell_checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+	webkit_spell_checker_learn_word (spell_checker, word);
+
+	g_free (word);
+}
+
+static void
+action_context_spell_ignore_cb (GtkAction *action,
+                                EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	WebKitSpellChecker *spell_checker;
+	gchar *word;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+		return;
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+	word = webkit_dom_range_get_text (range);
+
+	spell_checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+	webkit_spell_checker_ignore_word (spell_checker, word);
+
+	g_free (word);
+}
+
+static void
+action_copy_cb (GtkAction *action,
+                EEditor *editor)
+{
+	webkit_web_view_copy_clipboard (
+		WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+}
+
+static void
+action_cut_cb (GtkAction *action,
+               EEditor *editor)
+{
+	webkit_web_view_cut_clipboard (
+		WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+}
+
+static void
+action_find_cb (GtkAction *action,
+                EEditor *editor)
+{
+	gboolean found;
+
+
+	found = webkit_web_view_search_text (
+		WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)),
+		gtk_entry_get_text (GTK_ENTRY (WIDGET (FIND_ENTRY))),
+		gtk_toggle_button_get_active (
+			GTK_TOGGLE_BUTTON (WIDGET (FIND_CASE_SENSITIVE))),
+		!gtk_toggle_button_get_active (
+			GTK_TOGGLE_BUTTON (WIDGET (FIND_BACKWARDS))),
+		gtk_toggle_button_get_active (
+			GTK_TOGGLE_BUTTON (WIDGET (FIND_WRAP))));
+
+	gtk_action_set_sensitive (ACTION (FIND), found);
+
+	if (!found)
+		gtk_label_set_label (
+			GTK_LABEL (WIDGET (FIND_RESULT_LABEL)),
+			N_("No match found"));
+}
+
+static void
+action_find_again_cb (GtkAction *action,
+                      EEditor *editor)
+{
+	/* FIXME WEBKIT
+	 * Verify that this actually works and if so, then just change the
+	 * callback and remove this function. If not, then we are in trouble...
+	 */
+
+	action_find_cb (action, editor);
+}
+
+
+static void
+action_find_and_replace_cb (GtkAction *action,
+                            EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMNode *start, *end;
+	WebKitDOMNodeList *children;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	WebKitDOMNode *node;
+
+	gulong length, i;
+	const gchar *needle, *replacement;
+	gboolean case_sensitive, backwards, wrap, only_selection;
+
+	needle = gtk_entry_get_text (GTK_ENTRY (WIDGET (REPLACE_ENTRY)));
+	replacement = gtk_entry_get_text (
+				GTK_ENTRY (WIDGET (REPLACE_WITH_ENTRY)));
+	case_sensitive = gtk_toggle_button_get_active (
+				GTK_TOGGLE_BUTTON (WIDGET (REPLACE_CASE_SENSITIVE)));
+	wrap = gtk_toggle_button_get_active (
+				GTK_TOGGLE_BUTTON (WIDGET (REPLACE_WRAP)));
+	backwards = gtk_toggle_button_get_active (
+				GTK_TOGGLE_BUTTON (WIDGET (REPLACE_BACKWARDS)));
+	only_selection = gtk_toggle_button_get_active (
+				GTK_TOGGLE_BUTTON (WIDGET (REPLACE_ONLY_SELECTION)));
+
+	/* XXX Port this if to native WebKit API if they ever implement
+	 * support for find&replace...unless this implementation
+	 * is better ;) */
+
+	/* FIXME WEBKIT This is not working at all */
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+		return;
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+	start = webkit_dom_range_get_start_container (range, NULL);
+
+	if (only_selection) {
+		end = webkit_dom_range_get_end_container (range, NULL);
+	} else {
+		end = NULL;
+	}
+
+	/* WEBKIT FIXME Implement me! */
+	/*
+	node = start;
+	while (node && (node != end)) {
+
+
+
+	}
+	*/
+
+	/*
+	html = gtkhtml_editor_get_html (editor);
+
+	gtk_widget_hide (WIDGET (REPLACE_WINDOW));
+
+	html_engine_replace (
+		html->engine,
+		gtk_entry_get_text (GTK_ENTRY (WIDGET (REPLACE_ENTRY))),
+		gtk_entry_get_text (GTK_ENTRY (WIDGET (REPLACE_WITH_ENTRY))),
+		gtk_toggle_button_get_active (
+			GTK_TOGGLE_BUTTON (WIDGET (REPLACE_CASE_SENSITIVE))),
+		!gtk_toggle_button_get_active (
+			GTK_TOGGLE_BUTTON (WIDGET (REPLACE_BACKWARDS))),
+		FALSE, replace_ask_cb, editor);
+	*/
+}
+
+/* FIXME WEBKIT
+static void
+replace_ask_cb (HTMLEngine *engine,
+                gpointer data)
+{
+	GtkhtmlEditor *editor = data;
+
+	gtk_window_present (GTK_WINDOW (WIDGET (REPLACE_CONFIRMATION_WINDOW)));
+}
+
+static void
+replace_answer (GtkhtmlEditor *editor,
+                HTMLReplaceQueryAnswer answer)
+{
+	GtkHTML *html;
+
+	html = gtkhtml_editor_get_html (editor);
+
+	if (html_engine_replace_do (html->engine, answer))
+		gtk_widget_hide (WIDGET (REPLACE_CONFIRMATION_WINDOW));
+}
+*/
+
+static void
+action_indent_cb (GtkAction *action,
+                  EEditor *editor)
+{
+	WebKitDOMDocument *document;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	webkit_dom_document_exec_command (document, "indent", FALSE, "");
+}
+
+static void
+action_insert_emoticon_cb (GtkAction *action,
+      EEditor *editor)
+{
+	/* FIXME WEBKIT
+	GtkHTML *html;
+	HTMLObject *image;
+	GtkIconInfo *icon_info;
+	GtkIconTheme *icon_theme;
+	GtkhtmlFaceChooser *chooser;
+	GtkhtmlFace *face;
+	const gchar *filename;
+	gchar *uri = NULL;
+
+	html = gtkhtml_editor_get_html (editor);
+
+	chooser = GTKHTML_FACE_CHOOSER (action);
+	face = gtkhtml_face_chooser_get_current_face (chooser);
+	g_return_if_fail (face != NULL);
+
+	icon_theme = gtk_icon_theme_get_default ();
+	icon_info = gtk_icon_theme_lookup_icon (
+		icon_theme, face->icon_name, 16, 0);
+	g_return_if_fail (icon_info != NULL);
+
+	filename = gtk_icon_info_get_filename (icon_info);
+	if (filename != NULL)
+		uri = g_filename_to_uri (filename, NULL, NULL);
+	gtk_icon_info_free (icon_info);
+	g_return_if_fail (uri != NULL);
+
+	image = html_image_new (
+		html_engine_get_image_factory (html->engine),
+		uri, NULL, NULL, -1, -1, FALSE, FALSE, 0, NULL,
+		HTML_VALIGN_MIDDLE, FALSE);
+	html_image_set_alt (HTML_IMAGE (image), face->text_face);
+	html_engine_paste_object (
+		html->engine, image, html_object_get_length (image));
+
+	g_free (uri);
+	*/
+}
+
+static void
+action_insert_html_file_cb (GtkToggleAction *action,
+                            EEditor *editor)
+{
+	GtkWidget *dialog;
+	GtkFileFilter *filter;
+
+	dialog = gtk_file_chooser_dialog_new (
+			_("Insert HTML File"), NULL,
+			GTK_FILE_CHOOSER_ACTION_OPEN,
+			GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+
+	filter = gtk_file_filter_new ();
+	gtk_file_filter_set_name (filter, _("HTML file"));
+	gtk_file_filter_add_mime_type (filter, "text/html");
+	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+		GFile *file = gtk_file_chooser_get_file (
+					GTK_FILE_CHOOSER (dialog));
+
+		/* XXX Need a way to cancel this. */
+		g_file_load_contents_async (
+			file, NULL, (GAsyncReadyCallback)
+			insert_html_file_ready_cb,
+			g_object_ref (editor));
+
+		g_object_unref (file);
+	}
+
+	gtk_widget_destroy (dialog);
+}
+
+static void
+action_insert_image_cb (GtkAction *action,
+                        EEditor *editor)
+{
+	GtkWidget *dialog;
+
+	dialog = e_image_chooser_dialog_new (
+			_("Insert Image"), NULL);
+
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+		gchar *uri;
+		EEditorSelection *selection;
+
+		 uri = gtk_file_chooser_get_uri (
+				GTK_FILE_CHOOSER (dialog));
+
+		 selection = e_editor_widget_get_selection (
+				e_editor_get_editor_widget (editor));
+		 e_editor_selection_insert_image (selection, uri);
+		 g_free (uri);
+	}
+
+	gtk_widget_destroy (dialog);
+}
+
+static void
+action_insert_link_cb (GtkAction *action,
+                       EEditor *editor)
+{
+	/* FIXME WEBKIT */
+	gtk_window_present (GTK_WINDOW (WIDGET (LINK_PROPERTIES_WINDOW)));
+}
+
+static void
+action_insert_rule_cb (GtkAction *action,
+                       EEditor *editor)
+{
+	WebKitDOMDocument *document;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	webkit_dom_document_exec_command (
+		document, "insertHorizontalRule", FALSE, "");
+
+	/* FIXME WEBKIT - does the action work? */
+	gtk_action_activate (ACTION (PROPERTIES_RULE));
+}
+
+static void
+action_insert_table_cb (GtkAction *action,
+                        EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMElement *table;
+	WebKitDOMDOMWindow *window;
+	WebKitDOMDOMSelection *selection;
+	WebKitDOMRange *range;
+	gint i;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	window = webkit_dom_document_get_default_view (document);
+	selection = webkit_dom_dom_window_get_selection (window);
+	if (webkit_dom_dom_selection_get_range_count (selection) < 1)
+		return;
+
+	range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+
+	/* Default 3x3 table */
+	table = webkit_dom_document_create_element (document, "TABLE", NULL);
+	for (i = 0; i < 3; i++) {
+		WebKitDOMHTMLElement *row;
+		gint j;
+
+		row = webkit_dom_html_table_element_insert_row (
+			WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
+
+		for (j = 0; j < 3; j++) {
+			webkit_dom_html_table_row_element_insert_cell (
+				WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row),
+				-1, NULL);
+		}
+	}
+
+	webkit_dom_range_insert_node (range, WEBKIT_DOM_NODE (table), NULL);
+
+	/* FIXME WEBKIT - does the action work? */
+	gtk_action_activate (ACTION (PROPERTIES_TABLE));
+}
+
+static void
+action_insert_text_file_cb (GtkAction *action,
+                            EEditor *editor)
+{
+	GtkWidget *dialog;
+	GtkFileFilter *filter;
+
+	dialog = gtk_file_chooser_dialog_new (
+			_("Insert text file"), NULL,
+			GTK_FILE_CHOOSER_ACTION_OPEN,
+			GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+
+	filter = gtk_file_filter_new ();
+	gtk_file_filter_set_name (filter, _("Text file"));
+	gtk_file_filter_add_mime_type (filter, "text/plain");
+	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+		GFile *file = gtk_file_chooser_get_file (
+					GTK_FILE_CHOOSER (dialog));
+
+		/* XXX Need a way to cancel this. */
+		g_file_load_contents_async (
+			file, NULL, (GAsyncReadyCallback)
+			insert_text_file_ready_cb,
+			g_object_ref (editor));
+
+		g_object_unref (file);
+	}
+
+	gtk_widget_destroy (dialog);
+}
+
+static void
+action_italic_cb (GtkToggleAction *action,
+                  EEditor *editor)
+{
+	EEditorSelection *selection;
+
+	selection = e_editor_widget_get_selection (
+			e_editor_get_editor_widget (editor));
+	e_editor_selection_set_italic (
+		selection, gtk_toggle_action_get_active (action));
+}
+
+static void
+action_justify_cb (GtkRadioAction *action,
+                   GtkRadioAction *current,
+                   EEditor *editor)
+{
+	WebKitDOMDocument *document;
+	const gchar *command;
+
+	document = webkit_web_view_get_dom_document (
+			WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+
+	switch (gtk_radio_action_get_current_value (current))
+	{
+		case E_EDITOR_SELECTION_ALIGNMENT_CENTER:
+			command = "justifyCenter";
+			break;
+
+		case E_EDITOR_SELECTION_ALIGNMENT_LEFT:
+			command = "justifyLeft";
+			break;
+
+		case E_EDITOR_SELECTION_ALIGNMENT_RIGHT:
+			command = "justifyRight";
+			break;
+	}
+
+	webkit_dom_document_exec_command (document, command, FALSE, "");
+}
+
+static void
+action_language_cb (GtkToggleAction *action,
+                    EEditor *editor)
+{
+	/* FIXME WEBKIT */
+	/*
+	const GtkhtmlSpellLanguage *language;
+	GtkhtmlSpellChecker *checker;
+	const gchar *language_code;
+	GtkAction *add_action;
+	GtkHTML *html;
+	GList *list;
+	guint length;
+	gchar *action_name;
+	gboolean active;
+
+	active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+	language_code = gtk_action_get_name (GTK_ACTION (action));
+	language = gtkhtml_spell_language_lookup (language_code);
+
+	checker = g_hash_table_lookup (
+		editor->priv->available_spell_checkers, language);
+	g_return_if_fail (checker != NULL);
+
+	//Update the list of active spell checkers.
+	list = editor->priv->active_spell_checkers;
+	if (active)
+		list = g_list_insert_sorted (
+			list, g_object_ref (checker),
+			(GCompareFunc) gtkhtml_spell_checker_compare);
+	else {
+		GList *link;
+
+		link = g_list_find (list, checker);
+		g_return_if_fail (link != NULL);
+		list = g_list_delete_link (list, link);
+		g_object_unref (checker);
+	}
+	editor->priv->active_spell_checkers = list;
+	length = g_list_length (list);
+
+	// Update "Add Word To" context menu item visibility.
+	action_name = g_strdup_printf ("context-spell-add-%s", language_code);
+	add_action = gtkhtml_editor_get_action (editor, action_name);
+	gtk_action_set_visible (add_action, active);
+	g_free (action_name);
+
+	gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD), length == 1);
+	gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD_MENU), length > 1);
+	gtk_action_set_visible (ACTION (CONTEXT_SPELL_IGNORE), length > 0);
+
+	gtk_action_set_sensitive (ACTION (SPELL_CHECK), length > 0);
+
+	html = gtkhtml_editor_get_html (editor);
+	html_engine_spell_check (html->engine);
+
+	gtkthtml_editor_emit_spell_languages_changed (editor);
+	*/
+}
+
+static void
+action_mode_cb (GtkRadioAction *action,
+                GtkRadioAction *current,
+                EEditor *editor)
+{
+	/* FIXME WEBKIT */
+	/*
+	GtkActionGroup *action_group;
+	HTMLPainter *new_painter;
+	HTMLPainter *old_painter;
+	GtkHTML *html;
+	EditorMode mode;
+	gboolean html_mode;
+
+	html = gtkhtml_editor_get_html (editor);
+	mode = gtk_radio_action_get_current_value (current);
+	html_mode = (mode == EDITOR_MODE_HTML);
+
+	action_group = editor->priv->html_actions;
+	gtk_action_group_set_sensitive (action_group, html_mode);
+
+	action_group = editor->priv->html_context_actions;
+	gtk_action_group_set_visible (action_group, html_mode);
+
+	gtk_widget_set_sensitive (editor->priv->color_combo_box, html_mode);
+
+	if (html_mode)
+		gtk_widget_show (editor->priv->html_toolbar);
+	else
+		gtk_widget_hide (editor->priv->html_toolbar);
+
+	// Certain paragraph styles are HTML-only.
+	gtk_action_set_sensitive (ACTION (STYLE_H1), html_mode);
+	gtk_action_set_sensitive (ACTION (STYLE_H2), html_mode);
+	gtk_action_set_sensitive (ACTION (STYLE_H3), html_mode);
+	gtk_action_set_sensitive (ACTION (STYLE_H4), html_mode);
+	gtk_action_set_sensitive (ACTION (STYLE_H5), html_mode);
+	gtk_action_set_sensitive (ACTION (STYLE_H6), html_mode);
+	gtk_action_set_sensitive (ACTION (STYLE_ADDRESS), html_mode);
+
+	// Swap painters.
+
+	if (html_mode) {
+		new_painter = editor->priv->html_painter;
+		old_painter = editor->priv->plain_painter;
+	} else {
+		new_painter = editor->priv->plain_painter;
+		old_painter = editor->priv->html_painter;
+	}
+
+	// Might be true during initialization.
+	if (html->engine->painter == new_painter)
+		return;
+
+	html_gdk_painter_unrealize (HTML_GDK_PAINTER (old_painter));
+	if (html->engine->window != NULL)
+		html_gdk_painter_realize (
+			HTML_GDK_PAINTER (new_painter),
+			html->engine->window);
+
+	html_font_manager_set_default (
+		&new_painter->font_manager,
+		old_painter->font_manager.variable.face,
+		old_painter->font_manager.fixed.face,
+		old_painter->font_manager.var_size,
+		old_painter->font_manager.var_points,
+		old_painter->font_manager.fix_size,
+		old_painter->font_manager.fix_points);
+
+	html_engine_set_painter (html->engine, new_painter);
+	html_engine_schedule_redraw (html->engine);
+
+	g_object_notify (G_OBJECT (editor), "html-mode");
+	*/
+}
+
+static void
+action_monospaced_cb (GtkToggleAction *action,
+                      EEditor *editor)
+{
+	/* FIXME WEBKIT */
+	/*
+	GtkHTML *html;
+	GtkHTMLFontStyle and_mask;
+	GtkHTMLFontStyle or_mask;
+
+	if (editor->priv->ignore_style_change)
+		return;
+
+	if (gtk_toggle_action_get_active (action)) {
+		and_mask = GTK_HTML_FONT_STYLE_MAX;
+		or_mask = GTK_HTML_FONT_STYLE_FIXED;
+	} else {
+		and_mask = ~GTK_HTML_FONT_STYLE_FIXED;
+		or_mask = 0;
+	}
+
+	html = gtkhtml_editor_get_html (editor);
+	gtk_html_set_font_style (html, and_mask, or_mask);
+	*/
+}
+
+static void
+action_paste_cb (GtkAction *action,
+                 EEditor *editor)
+{
+	webkit_web_view_paste_clipboard (
+		WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+}
+
+static void
+action_paste_quote_cb (GtkAction *action,
+                       EEditor *editor)
+{
+	/* FIXME WEBKIT */
+	/*
+	GtkHTML *html;
+
+	html = gtkhtml_editor_get_html (editor);
+	gtk_html_paste (html, TRUE);
+	*/
+}
+
+static void
+action_properties_cell_cb (GtkAction *action,
+                           EEditor *editor)
+{
+	gtk_window_present (GTK_WINDOW (WIDGET (CELL_PROPERTIES_WINDOW)));
+}
+
+static void
+action_properties_image_cb (GtkAction *action,
+                            EEditor *editor)
+{
+	gtk_window_present (GTK_WINDOW (WIDGET (IMAGE_PROPERTIES_WINDOW)));
+}
+
+static void
+action_properties_link_cb (GtkAction *action,
+                           EEditor *editor)
+{
+	gtk_window_present (GTK_WINDOW (WIDGET (LINK_PROPERTIES_WINDOW)));
+}
+
+static void
+action_properties_page_cb (GtkAction *action,
+                           EEditor *editor)
+{
+	gtk_window_present (GTK_WINDOW (WIDGET (PAGE_PROPERTIES_WINDOW)));
+}
+
+static void
+action_properties_paragraph_cb (GtkAction *action,
+                                EEditor *editor)
+{
+	gtk_window_present (GTK_WINDOW (WIDGET (PARAGRAPH_PROPERTIES_WINDOW)));
+}
+
+static void
+action_properties_rule_cb (GtkAction *action,
+                           EEditor *editor)
+{
+	gtk_window_present (GTK_WINDOW (WIDGET (RULE_PROPERTIES_WINDOW)));
+}
+
+static void
+action_properties_table_cb (GtkAction *action,
+                            EEditor *editor)
+{
+	gtk_window_present (GTK_WINDOW (WIDGET (TABLE_PROPERTIES_WINDOW)));
+}
+
+static void
+action_properties_text_cb (GtkAction *action,
+                           EEditor *editor)
+{
+	gtk_window_present (GTK_WINDOW (WIDGET (TEXT_PROPERTIES_WINDOW)));
+}
+
+static void
+action_style_cb (GtkRadioAction *action,
+                 GtkRadioAction *current,
+                 EEditor *editor)
+{
+	EEditorSelection *selection;
+
+	/* FIXME WEBKIT What's this good for? */
+	if (editor->priv->ignore_style_change)
+		return;
+
+	selection = e_editor_widget_get_selection (
+			e_editor_get_editor_widget (editor));
+	e_editor_selection_set_block_format (
+		selection, gtk_radio_action_get_current_value (current));
+}
+
+static void
+action_redo_cb (GtkAction *action,
+                EEditor *editor)
+{
+	webkit_web_view_redo (
+		WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+}
+
+static void
+action_select_all_cb (GtkAction *action,
+                      EEditor *editor)
+{
+	webkit_web_view_select_all (
+		WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+}
+
+static void
+action_show_find_cb (GtkAction *action,
+                     EEditor *editor)
+{
+	gtk_widget_set_sensitive (WIDGET (FIND_BUTTON), TRUE);
+
+	gtk_window_present (GTK_WINDOW (WIDGET (FIND_WINDOW)));
+}
+
+static void
+action_show_replace_cb (GtkAction *action,
+                        EEditor *editor)
+{
+	gtk_window_present (GTK_WINDOW (WIDGET (REPLACE_WINDOW)));
+}
+
+static void
+action_size_cb (GtkRadioAction *action,
+                GtkRadioAction *current,
+                EEditor *editor)
+{
+	EEditorSelection *selection;
+
+	if (editor->priv->ignore_style_change)
+		return;
+
+	selection = e_editor_widget_get_selection (
+			e_editor_get_editor_widget (editor));
+	e_editor_selection_set_font_size (
+		selection, gtk_radio_action_get_current_value (current));
+}
+
+static void
+action_spell_check_cb (GtkAction *action,
+                       EEditor *editor)
+{
+	/* FIXME WEBKIT
+	e_editor_widget_spell_check (editor);
+	*/
+}
+
+static void
+action_strikethrough_cb (GtkToggleAction *action,
+                         EEditor *editor)
+{
+	EEditorSelection *selection;
+
+	if (editor->priv->ignore_style_change)
+		return;
+
+	selection = e_editor_widget_get_selection (
+			e_editor_get_editor_widget (editor));
+	e_editor_selection_set_italic (
+		selection, gtk_toggle_action_get_active (action));
+}
+
+static void
+action_test_url_cb (GtkAction *action,
+                    EEditor *editor)
+{
+	/* FIXME WEBKIT What does this do? */
+	/*
+	GtkEntry *entry;
+	GtkWindow *window;
+	const gchar *text;
+
+	entry = GTK_ENTRY (WIDGET (LINK_PROPERTIES_URL_ENTRY));
+	window = GTK_WINDOW (WIDGET (LINK_PROPERTIES_WINDOW));
+
+	text = gtk_entry_get_text (entry);
+
+	if (text != NULL && *text != '\0')
+		gtkhtml_editor_show_uri (window, text);
+	*/
+}
+
+static void
+action_underline_cb (GtkToggleAction *action,
+                     EEditor *editor)
+{
+	EEditorSelection *selection;
+
+	if (editor->priv->ignore_style_change)
+		return;
+
+	selection = e_editor_widget_get_selection (
+			e_editor_get_editor_widget (editor));
+	e_editor_selection_set_underline (
+		selection, gtk_toggle_action_get_active (action));
+}
+
+static void
+action_undo_cb (GtkAction *action,
+                EEditor *editor)
+{
+	webkit_web_view_undo (
+		WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+}
+
+static void
+action_unindent_cb (GtkAction *action,
+                    EEditor *editor)
+{
+	WebKitDOMDocument *document;
+
+	document = webkit_web_view_get_dom_document (
+		WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
+	webkit_dom_document_exec_command (
+		document, "outdent", FALSE, "");
+}
+
+static void
+action_wrap_lines_cb (GtkAction *action,
+                      EEditor *editor)
+{
+	/* FIXME WEBKIT */
+	/*
+	e_editor_widget_wrap_lines (editor);
+	*/
+}
+
+/*****************************************************************************
+ * Core Actions
+ *
+ * These actions are always enabled.
+ *****************************************************************************/
+
+static GtkActionEntry core_entries[] = {
+
+	{ "confirm-replace",
+	  NULL,
+	  N_("_Replace"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_confirm_replace_cb) },
+
+	{ "confirm-replace-all",
+	  NULL,
+	  N_("Replace _All"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_confirm_replace_all_cb) },
+
+	{ "confirm-replace-cancel",
+	  GTK_STOCK_CLOSE,
+	  NULL,
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_confirm_replace_cancel_cb) },
+
+	{ "confirm-replace-next",
+	  NULL,
+	  N_("_Next"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_confirm_replace_next_cb) },
+
+	{ "copy",
+	  GTK_STOCK_COPY,
+	  N_("_Copy"),
+	  "<Control>c",
+	  NULL,
+	  G_CALLBACK (action_copy_cb) },
+
+	{ "cut",
+	  GTK_STOCK_CUT,
+	  N_("Cu_t"),
+	  "<Control>x",
+	  NULL,
+	  G_CALLBACK (action_cut_cb) },
+
+	{ "find",
+	  GTK_STOCK_FIND,
+	  NULL,
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_find_cb) },
+
+	{ "find-again",
+	  NULL,
+	  N_("Find A_gain"),
+	  "<Control>g",
+	  NULL,
+	  G_CALLBACK (action_find_again_cb) },
+
+	{ "find-and-replace",
+	  GTK_STOCK_FIND_AND_REPLACE,
+	  NULL,
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_find_and_replace_cb) },
+
+	{ "indent",
+	  GTK_STOCK_INDENT,
+	  N_("_Increase Indent"),
+	  "<Control>bracketright",
+	  N_("Increase Indent"),
+	  G_CALLBACK (action_indent_cb) },
+
+	{ "insert-html-file",
+	  NULL,
+	  N_("_HTML File..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_insert_html_file_cb) },
+
+	{ "insert-text-file",
+	  NULL,
+	  N_("Te_xt File..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_insert_text_file_cb) },
+
+	{ "paste",
+	  GTK_STOCK_PASTE,
+	  N_("_Paste"),
+	  "<Control>v",
+	  NULL,
+	  G_CALLBACK (action_paste_cb) },
+
+	{ "paste-quote",
+	  NULL,
+	  N_("Paste _Quotation"),
+	  "<Shift><Control>v",
+	  NULL,
+	  G_CALLBACK (action_paste_quote_cb) },
+
+	{ "redo",
+	  GTK_STOCK_REDO,
+	  N_("_Redo"),
+	  "<Shift><Control>z",
+	  NULL,
+	  G_CALLBACK (action_redo_cb) },
+
+	{ "select-all",
+	  GTK_STOCK_SELECT_ALL,
+	  N_("Select _All"),
+	  "<Control>a",
+	  NULL,
+	  G_CALLBACK (action_select_all_cb) },
+
+	{ "show-find",
+	  GTK_STOCK_FIND,
+	  N_("_Find..."),
+	  "<Control>f",
+	  NULL,
+	  G_CALLBACK (action_show_find_cb) },
+
+	{ "show-replace",
+	  GTK_STOCK_FIND_AND_REPLACE,
+	  N_("Re_place..."),
+	  "<Control>h",
+	  NULL,
+	  G_CALLBACK (action_show_replace_cb) },
+
+	{ "spell-check",
+	  GTK_STOCK_SPELL_CHECK,
+	  N_("Check _Spelling..."),
+	  "F7",
+	  NULL,
+	  G_CALLBACK (action_spell_check_cb) },
+
+	{ "test-url",
+	  NULL,
+	  N_("_Test URL..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_test_url_cb) },
+
+	{ "undo",
+	  GTK_STOCK_UNDO,
+	  N_("_Undo"),
+	  "<Control>z",
+	  NULL,
+	  G_CALLBACK (action_undo_cb) },
+
+	{ "unindent",
+	  GTK_STOCK_UNINDENT,
+	  N_("_Decrease Indent"),
+	  "<Control>bracketleft",
+	  N_("Decrease Indent"),
+	  G_CALLBACK (action_unindent_cb) },
+
+	{ "wrap-lines",
+	  NULL,
+	  N_("_Wrap Lines"),
+	  "<Control>k",
+	  NULL,
+	  G_CALLBACK (action_wrap_lines_cb) },
+
+	/* Menus */
+
+	{ "edit-menu",
+	  NULL,
+	  N_("_Edit"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	{ "file-menu",
+	  NULL,
+	  N_("_File"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	{ "format-menu",
+	  NULL,
+	  N_("For_mat"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	{ "paragraph-style-menu",
+	  NULL,
+	  N_("_Paragraph Style"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	{ "insert-menu",
+	  NULL,
+	  N_("_Insert"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	{ "justify-menu",
+	  NULL,
+	  N_("_Alignment"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	{ "language-menu",
+	  NULL,
+	  N_("Current _Languages"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	{ "view-menu",
+	  NULL,
+	  N_("_View"),
+	  NULL,
+	  NULL,
+	  NULL }
+};
+
+static GtkRadioActionEntry core_justify_entries[] = {
+
+	{ "justify-center",
+	  GTK_STOCK_JUSTIFY_CENTER,
+	  N_("_Center"),
+	  "<Control>e",
+	  N_("Center Alignment"),
+	  E_EDITOR_SELECTION_ALIGNMENT_CENTER },
+
+	{ "justify-left",
+	  GTK_STOCK_JUSTIFY_LEFT,
+	  N_("_Left"),
+	  "<Control>l",
+	  N_("Left Alignment"),
+	  E_EDITOR_SELECTION_ALIGNMENT_LEFT },
+
+	{ "justify-right",
+	  GTK_STOCK_JUSTIFY_RIGHT,
+	  N_("_Right"),
+	  "<Control>r",
+	  N_("Right Alignment"),
+	  E_EDITOR_SELECTION_ALIGNMENT_RIGHT }
+};
+
+static GtkRadioActionEntry core_mode_entries[] = {
+
+	{ "mode-html",
+	  NULL,
+	  N_("_HTML"),
+	  NULL,
+	  N_("HTML editing mode"),
+	  E_EDITOR_WIDGET_MODE_HTML },
+
+	{ "mode-plain",
+	  NULL,
+	  N_("Plain _Text"),
+	  NULL,
+	  N_("Plain text editing mode"),
+	  E_EDITOR_WIDGET_MODE_PLAIN_TEXT }
+};
+
+static GtkRadioActionEntry core_style_entries[] = {
+
+	{ "style-normal",
+	  NULL,
+	  N_("_Normal"),
+	  "<Control>0",
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH },
+
+	{ "style-h1",
+	  NULL,
+	  N_("Header _1"),
+	  "<Control>1",
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_H1 },
+
+	{ "style-h2",
+	  NULL,
+	  N_("Header _2"),
+	  "<Control>2",
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_H2 },
+
+	{ "style-h3",
+	  NULL,
+	  N_("Header _3"),
+	  "<Control>3",
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_H3 },
+
+	{ "style-h4",
+	  NULL,
+	  N_("Header _4"),
+	  "<Control>4",
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_H4 },
+
+	{ "style-h5",
+	  NULL,
+	  N_("Header _5"),
+	  "<Control>5",
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_H5 },
+
+	{ "style-h6",
+	  NULL,
+	  N_("Header _6"),
+	  "<Control>6",
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_H6 },
+
+	{ "style-address",
+	  NULL,
+	  N_("A_ddress"),
+	  "<Control>8",
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS },
+
+	{ "style-preformat",
+	  NULL,
+	  N_("_Preformatted"),
+	  "<Control>7",
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_PRE },
+
+	{ "style-list-bullet",
+	  NULL,
+	  N_("_Bulleted List"),
+	  NULL,
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST },
+
+	{ "style-list-roman",
+	  NULL,
+	  N_("_Roman Numeral List"),
+	  NULL,
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN },
+
+	{ "style-list-number",
+	  NULL,
+	  N_("Numbered _List"),
+	  NULL,
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST },
+
+	{ "style-list-alpha",
+	  NULL,
+	  N_("_Alphabetical List"),
+	  NULL,
+	  NULL,
+	  E_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA }
+};
+
+/*****************************************************************************
+ * Core Actions (HTML only)
+ *
+ * These actions are only enabled in HTML mode.
+ *****************************************************************************/
+
+static GtkActionEntry html_entries[] = {
+
+	{ "insert-image",
+	  "insert-image",
+	  N_("_Image..."),
+	  NULL,
+	  N_("Insert Image"),
+	  G_CALLBACK (action_insert_image_cb) },
+
+	{ "insert-link",
+	  "insert-link",
+	  N_("_Link..."),
+	  NULL,
+	  N_("Insert Link"),
+	  G_CALLBACK (action_insert_link_cb) },
+
+	{ "insert-rule",
+	  "stock_insert-rule",
+	  /* Translators: 'Rule' here means a horizontal line in an HTML text */
+	  N_("_Rule..."),
+	  NULL,
+	  /* Translators: 'Rule' here means a horizontal line in an HTML text */
+	  N_("Insert Rule"),
+	  G_CALLBACK (action_insert_rule_cb) },
+
+	{ "insert-table",
+	  "stock_insert-table",
+	  N_("_Table..."),
+	  NULL,
+	  N_("Insert Table"),
+	  G_CALLBACK (action_insert_table_cb) },
+
+	{ "properties-cell",
+	  NULL,
+	  N_("_Cell..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_cell_cb) },
+
+	{ "properties-image",
+	  NULL,
+	  N_("_Image..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_image_cb) },
+
+	{ "properties-link",
+	  NULL,
+	  N_("_Link..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_link_cb) },
+
+	{ "properties-page",
+	  NULL,
+	  N_("Pa_ge..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_page_cb) },
+
+	{ "properties-rule",
+	  NULL,
+	  /* Translators: 'Rule' here means a horizontal line in an HTML text */
+	  N_("_Rule..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_rule_cb) },
+
+	{ "properties-table",
+	  NULL,
+	  N_("_Table..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_table_cb) },
+
+	/* Menus */
+
+	{ "font-size-menu",
+	  NULL,
+	  N_("Font _Size"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	{ "font-style-menu",
+	  NULL,
+	  N_("_Font Style"),
+	  NULL,
+	  NULL,
+	  NULL },
+};
+
+static GtkToggleActionEntry html_toggle_entries[] = {
+
+	{ "bold",
+	  GTK_STOCK_BOLD,
+	  N_("_Bold"),
+	  "<Control>b",
+	  N_("Bold"),
+	  G_CALLBACK (action_bold_cb),
+	  FALSE },
+
+	{ "italic",
+	  GTK_STOCK_ITALIC,
+	  N_("_Italic"),
+	  "<Control>i",
+	  N_("Italic"),
+	  G_CALLBACK (action_italic_cb),
+	  FALSE },
+
+	{ "monospaced",
+	  "stock_text-monospaced",
+	  N_("_Plain Text"),
+	  "<Control>t",
+	  N_("Plain Text"),
+	  G_CALLBACK (action_monospaced_cb),
+	  FALSE },
+
+	{ "strikethrough",
+	  GTK_STOCK_STRIKETHROUGH,
+	  N_("_Strikethrough"),
+	  NULL,
+	  N_("Strikethrough"),
+	  G_CALLBACK (action_strikethrough_cb),
+	  FALSE },
+
+	{ "underline",
+	  GTK_STOCK_UNDERLINE,
+	  N_("_Underline"),
+	  "<Control>u",
+	  N_("Underline"),
+	  G_CALLBACK (action_underline_cb),
+	  FALSE }
+};
+
+static GtkRadioActionEntry html_size_entries[] = {
+
+	{ "size-minus-two",
+	  NULL,
+	  /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+	  N_("-2"),
+	  NULL,
+	  NULL,
+	  E_EDITOR_SELECTION_FONT_SIZE_TINY },
+
+	{ "size-minus-one",
+	  NULL,
+	  /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+	  N_("-1"),
+	  NULL,
+	  NULL,
+	  E_EDITOR_SELECTION_FONT_SIZE_SMALL },
+
+	{ "size-plus-zero",
+	  NULL,
+	  /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+	  N_("+0"),
+	  NULL,
+	  NULL,
+	  E_EDITOR_SELECTION_FONT_SIZE_NORMAL },
+
+	{ "size-plus-one",
+	  NULL,
+	  /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+	  N_("+1"),
+	  NULL,
+	  NULL,
+	  E_EDITOR_SELECTION_FONT_SIZE_BIG },
+
+	{ "size-plus-two",
+	  NULL,
+	  /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+	  N_("+2"),
+	  NULL,
+	  NULL,
+	  E_EDITOR_SELECTION_FONT_SIZE_BIGGER },
+
+	{ "size-plus-three",
+	  NULL,
+	  /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+	  N_("+3"),
+	  NULL,
+	  NULL,
+	  E_EDITOR_SELECTION_FONT_SIZE_LARGE },
+
+	{ "size-plus-four",
+	  NULL,
+	  /* Translators: This is a font size level. It is shown on a tool bar. Please keep it as short as possible. */
+	  N_("+4"),
+	  NULL,
+	  NULL,
+	  E_EDITOR_SELECTION_FONT_SIZE_VERY_LARGE }
+};
+
+/*****************************************************************************
+ * Context Menu Actions
+ *
+ * These require separate action entries so we can toggle their visiblity
+ * rather than their sensitivity as we do with main menu / toolbar actions.
+ * Note that some of these actions use the same callback function as their
+ * non-context sensitive counterparts.
+ *****************************************************************************/
+
+static GtkActionEntry context_entries[] = {
+
+	{ "context-delete-cell",
+	  NULL,
+	  N_("Cell Contents"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_context_delete_cell_cb) },
+
+	{ "context-delete-column",
+	  NULL,
+	  N_("Column"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_context_delete_column_cb) },
+
+	{ "context-delete-row",
+	  NULL,
+	  N_("Row"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_context_delete_row_cb) },
+
+	{ "context-delete-table",
+	  NULL,
+	  N_("Table"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_context_delete_table_cb) },
+
+	/* Menus */
+
+	{ "context-delete-table-menu",
+	  NULL,
+	  /* Translators: Popup menu item caption, containing all the Delete options for a table */
+	  N_("Table Delete"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	{ "context-input-methods-menu",
+	  NULL,
+	  N_("Input Methods"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	{ "context-insert-table-menu",
+	  NULL,
+	  /* Translators: Popup menu item caption, containing all the Insert options for a table */
+	  N_("Table Insert"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	{ "context-properties-menu",
+	  NULL,
+	  N_("Properties"),
+	  NULL,
+	  NULL,
+	  NULL },
+};
+
+/*****************************************************************************
+ * Context Menu Actions (HTML only)
+ *
+ * These actions are never visible in plain-text mode.  Note that some
+ * of them use the same callback function as their non-context sensitive
+ * counterparts.
+ *****************************************************************************/
+
+static GtkActionEntry html_context_entries[] = {
+
+	{ "context-insert-column-after",
+	  NULL,
+	  N_("Column After"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_context_insert_column_after_cb) },
+
+	{ "context-insert-column-before",
+	  NULL,
+	  N_("Column Before"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_context_insert_column_before_cb) },
+
+	{ "context-insert-link",
+	  NULL,
+	  N_("Insert _Link"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_insert_link_cb) },
+
+	{ "context-insert-row-above",
+	  NULL,
+	  N_("Row Above"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_context_insert_row_above_cb) },
+
+	{ "context-insert-row-below",
+	  NULL,
+	  N_("Row Below"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_context_insert_row_below_cb) },
+
+	{ "context-insert-table",
+	  NULL,
+	  N_("Table"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_insert_table_cb) },
+
+	{ "context-properties-cell",
+	  NULL,
+	  N_("Cell..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_cell_cb) },
+
+	{ "context-properties-image",
+	  NULL,
+	  N_("Image..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_image_cb) },
+
+	{ "context-properties-link",
+	  NULL,
+	  N_("Link..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_link_cb) },
+
+	{ "context-properties-page",
+	  NULL,
+	  N_("Page..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_page_cb) },
+
+	{ "context-properties-paragraph",
+	  NULL,
+	  N_("Paragraph..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_paragraph_cb) },
+
+	{ "context-properties-rule",
+	  NULL,
+	  /* Translators: 'Rule' here means a horizontal line in an HTML text */
+	  N_("Rule..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_rule_cb) },
+
+	{ "context-properties-table",
+	  NULL,
+	  N_("Table..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_table_cb) },
+
+	{ "context-properties-text",
+	  NULL,
+	  N_("Text..."),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_properties_text_cb) },
+
+	{ "context-remove-link",
+	  NULL,
+	  N_("Remove Link"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_context_remove_link_cb) }
+};
+
+/*****************************************************************************
+ * Context Menu Actions (spell checking only)
+ *
+ * These actions are only visible when the word underneath the cursor is
+ * misspelled.
+ *****************************************************************************/
+
+static GtkActionEntry spell_context_entries[] = {
+
+	{ "context-spell-add",
+	  NULL,
+	  N_("Add Word to Dictionary"),
+	  NULL,
+	  NULL,
+          G_CALLBACK (action_context_spell_add_cb) },
+
+	{ "context-spell-ignore",
+	  NULL,
+	  N_("Ignore Misspelled Word"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_context_spell_ignore_cb) },
+
+	{ "context-spell-add-menu",
+	  NULL,
+	  N_("Add Word To"),
+	  NULL,
+	  NULL,
+	  NULL },
+
+	/* Menus */
+
+	{ "context-more-suggestions-menu",
+	  NULL,
+	  N_("More Suggestions"),
+	  NULL,
+	  NULL,
+	  NULL }
+};
+
+static void
+editor_actions_setup_languages_menu (EEditor *editor)
+{
+	/* FIXME WEBKIT
+	GtkUIManager *manager;
+	GtkActionGroup *action_group;
+	const GList *available_languages;
+	guint merge_id;
+
+	manager = editor->priv->manager;
+	action_group = editor->priv->language_actions;
+	available_languages = gtkhtml_spell_language_get_available ();
+	merge_id = gtk_ui_manager_new_merge_id (manager);
+
+	while (available_languages != NULL) {
+		GtkhtmlSpellLanguage *language = available_languages->data;
+		GtkhtmlSpellChecker *checker;
+		GtkToggleAction *action;
+
+		checker = gtkhtml_spell_checker_new (language);
+
+		g_hash_table_insert (
+			editor->priv->available_spell_checkers,
+			language, checker);
+
+		action = gtk_toggle_action_new (
+			gtkhtml_spell_language_get_code (language),
+			gtkhtml_spell_language_get_name (language),
+			NULL, NULL);
+
+		g_signal_connect (
+			action, "toggled",
+			G_CALLBACK (action_language_cb), editor);
+
+		gtk_action_group_add_action (
+			action_group, GTK_ACTION (action));
+
+		g_object_unref (action);
+
+		gtk_ui_manager_add_ui (
+			manager, merge_id,
+			"/main-menu/edit-menu/language-menu",
+			gtkhtml_spell_language_get_code (language),
+			gtkhtml_spell_language_get_code (language),
+			GTK_UI_MANAGER_AUTO, FALSE);
+
+		available_languages = g_list_next (available_languages);
+	}
+	*/
+}
+
+static void
+editor_actions_setup_spell_check_menu (EEditor *editor)
+{
+	/*
+	GtkUIManager *manager;
+	GtkActionGroup *action_group;
+	const GList *available_languages;
+	guint merge_id;
+
+	manager = editor->priv->manager;
+	action_group = editor->priv->spell_check_actions;;
+	available_languages = gtkhtml_spell_language_get_available ();
+	merge_id = gtk_ui_manager_new_merge_id (manager);
+
+	while (available_languages != NULL) {
+		GtkhtmlSpellLanguage *language = available_languages->data;
+		GtkAction *action;
+		const gchar *code;
+		const gchar *name;
+		gchar *action_label;
+		gchar *action_name;
+
+		code = gtkhtml_spell_language_get_code (language);
+		name = gtkhtml_spell_language_get_name (language);
+
+		// Add a suggestion menu. 
+
+		action_name = g_strdup_printf (
+			"context-spell-suggest-%s-menu", code);
+
+		action = gtk_action_new (action_name, name, NULL, NULL);
+		gtk_action_group_add_action (action_group, action);
+		g_object_unref (action);
+
+		gtk_ui_manager_add_ui (
+			manager, merge_id,
+			"/context-menu/context-spell-suggest",
+			action_name, action_name,
+			GTK_UI_MANAGER_MENU, FALSE);
+
+		g_free (action_name);
+
+		// Add an item to the "Add Word To" menu.
+
+		action_name = g_strdup_printf ("context-spell-add-%s", code);
+		// Translators: %s will be replaced with the actual dictionary name,
+		//where a user can add a word to. This is part of an "Add Word To" submenu.
+		action_label = g_strdup_printf (_("%s Dictionary"), name);
+
+		action = gtk_action_new (
+			action_name, action_label, NULL, NULL);
+
+		g_signal_connect (
+			action, "activate",
+			G_CALLBACK (action_context_spell_add_cb), editor);
+
+		// Visibility is dependent on whether the
+		//corresponding language action is active.
+		gtk_action_set_visible (action, FALSE);
+
+		gtk_action_group_add_action (action_group, action);
+
+		g_object_unref (action);
+
+		gtk_ui_manager_add_ui (
+			manager, merge_id,
+			"/context-menu/context-spell-add-menu",
+			action_name, action_name,
+			GTK_UI_MANAGER_AUTO, FALSE);
+
+		g_free (action_label);
+		g_free (action_name);
+
+		available_languages = g_list_next (available_languages);
+	}
+	*/
+}
+
+void
+editor_actions_init (EEditor *editor)
+{
+	GtkAction *action;
+	GtkActionGroup *action_group;
+	GtkUIManager *manager;
+	const gchar *domain;
+
+	g_return_if_fail (E_IS_EDITOR (editor));
+
+	manager = e_editor_get_ui_manager (editor);
+	domain = GETTEXT_PACKAGE;
+
+	/* Core Actions */
+	action_group = editor->priv->core_actions;
+	gtk_action_group_set_translation_domain (action_group, domain);
+	gtk_action_group_add_actions (
+		action_group, core_entries,
+		G_N_ELEMENTS (core_entries), editor);
+	gtk_action_group_add_radio_actions (
+		action_group, core_justify_entries,
+		G_N_ELEMENTS (core_justify_entries),
+		E_EDITOR_SELECTION_ALIGNMENT_LEFT,
+		G_CALLBACK (action_justify_cb), editor);
+	gtk_action_group_add_radio_actions (
+		action_group, core_mode_entries,
+		G_N_ELEMENTS (core_mode_entries),
+		E_EDITOR_WIDGET_MODE_HTML,
+		G_CALLBACK (action_mode_cb), editor);
+	gtk_action_group_add_radio_actions (
+		action_group, core_style_entries,
+		G_N_ELEMENTS (core_style_entries),
+		E_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH,
+		G_CALLBACK (action_style_cb), editor);
+	gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+	/* Face Action */
+	action = e_emoticon_action_new (
+		"insert-face", _("_Emoticon"),
+		_("Insert Emoticon"), NULL);
+	g_object_set (action, "icon-name", "face-smile", NULL);
+	g_signal_connect (
+		action, "item-activated",
+		G_CALLBACK (action_insert_emoticon_cb), editor);
+	gtk_action_group_add_action (action_group, action);
+
+	/* Core Actions (HTML only) */
+	action_group = editor->priv->html_actions;
+	gtk_action_group_set_translation_domain (action_group, domain);
+	gtk_action_group_add_actions (
+		action_group, html_entries,
+		G_N_ELEMENTS (html_entries), editor);
+	gtk_action_group_add_toggle_actions (
+		action_group, html_toggle_entries,
+		G_N_ELEMENTS (html_toggle_entries), editor);
+	gtk_action_group_add_radio_actions (
+		action_group, html_size_entries,
+		G_N_ELEMENTS (html_size_entries),
+		E_EDITOR_SELECTION_FONT_SIZE_NORMAL,
+		G_CALLBACK (action_size_cb), editor);
+	gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+	/* Context Menu Actions */
+	action_group = editor->priv->context_actions;
+	gtk_action_group_set_translation_domain (action_group, domain);
+	gtk_action_group_add_actions (
+		action_group, context_entries,
+		G_N_ELEMENTS (context_entries), editor);
+	gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+	/* Context Menu Actions (HTML only) */
+	action_group = editor->priv->html_context_actions;
+	gtk_action_group_set_translation_domain (action_group, domain);
+	gtk_action_group_add_actions (
+		action_group, html_context_entries,
+		G_N_ELEMENTS (html_context_entries), editor);
+	gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+	/* Context Menu Actions (spell check only) */
+	action_group = editor->priv->spell_check_actions;
+	gtk_action_group_set_translation_domain (action_group, domain);
+	gtk_action_group_add_actions (
+		action_group, spell_context_entries,
+		G_N_ELEMENTS (spell_context_entries), editor);
+	gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+	/* Language actions are generated dynamically. */
+	editor_actions_setup_languages_menu (editor);
+	action_group = editor->priv->language_actions;
+	gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+	/* Some spell check actions are generated dynamically. */
+	action_group = editor->priv->suggestion_actions;
+	editor_actions_setup_spell_check_menu (editor);
+	gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+	/* Fine Tuning */
+
+	g_object_set (
+		G_OBJECT (ACTION (FIND)),
+		"short-label", _("_Find"), NULL);
+	g_object_set (
+		G_OBJECT (ACTION (FIND_AND_REPLACE)),
+		"short-label", _("Re_place"), NULL);
+	g_object_set (
+		G_OBJECT (ACTION (INSERT_IMAGE)),
+		"short-label", _("_Image"), NULL);
+	g_object_set (
+		G_OBJECT (ACTION (INSERT_LINK)),
+		"short-label", _("_Link"), NULL);
+	g_object_set (
+		G_OBJECT (ACTION (INSERT_RULE)),
+		/* Translators: 'Rule' here means a horizontal line in an HTML text */
+		"short-label", _("_Rule"), NULL);
+	g_object_set (
+		G_OBJECT (ACTION (INSERT_TABLE)),
+		"short-label", _("_Table"), NULL);
+
+	gtk_action_set_sensitive (ACTION (UNINDENT), FALSE);
+}
+ 
diff --git a/e-util/e-editor-actions.h b/e-util/e-editor-actions.h
new file mode 100644
index 0000000..3f84b13
--- /dev/null
+++ b/e-util/e-editor-actions.h
@@ -0,0 +1,152 @@
+/* e-editor-actions.h
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EDITOR_ACTIONS_H
+#define E_EDITOR_ACTIONS_H
+
+#define E_EDITOR_ACTION(editor, name) \
+	(e_editor_get_action (E_EDITOR (editor), (name)))
+
+#define E_EDITOR_ACTION_BOLD(editor) \
+	E_EDITOR_ACTION ((editor), "bold")
+#define E_EDITOR_ACTION_CONFIRM_REPLACE(editor) \
+	E_EDITOR_ACTION ((editor), "confirm-replace")
+#define E_EDITOR_ACTION_CONFIRM_REPLACE_ALL(editor) \
+	E_EDITOR_ACTION ((editor), "confirm-replace-all")
+#define E_EDITOR_ACTION_CONFIRM_REPLACE_CANCEL(editor) \
+	E_EDITOR_ACTION ((editor), "confirm-replace-cancel")
+#define E_EDITOR_ACTION_CONFIRM_REPLACE_NEXT(editor) \
+	E_EDITOR_ACTION ((editor), "confirm-replace-next")
+#define E_EDITOR_ACTION_CONTEXT_DELETE_CELL(editor) \
+	E_EDITOR_ACTION ((editor), "context-delete-cell")
+#define E_EDITOR_ACTION_CONTEXT_DELETE_COLUMN(editor) \
+	E_EDITOR_ACTION ((editor), "context-delete-column")
+#define E_EDITOR_ACTION_CONTEXT_DELETE_ROW(editor) \
+	E_EDITOR_ACTION ((editor), "context-delete-row")
+#define E_EDITOR_ACTION_CONTEXT_DELETE_TABLE(editor) \
+	E_EDITOR_ACTION ((editor), "context-delete-table")
+#define E_EDITOR_ACTION_CONTEXT_INSERT_COLUMN_AFTER(editor) \
+	E_EDITOR_ACTION ((editor), "context-insert-column-after")
+#define E_EDITOR_ACTION_CONTEXT_INSERT_COLUMN_BEFORE(editor) \
+	E_EDITOR_ACTION ((editor), "context-insert-column-before")
+#define E_EDITOR_ACTION_CONTEXT_INSERT_ROW_ABOVE(editor) \
+	E_EDITOR_ACTION ((editor), "context-insert-row-above")
+#define E_EDITOR_ACTION_CONTEXT_INSERT_ROW_BELOW(editor) \
+	E_EDITOR_ACTION ((editor), "context-insert-row-below")
+#define E_EDITOR_ACTION_CONTEXT_INSERT_TABLE(editor) \
+	E_EDITOR_ACTION ((editor), "context-insert-table")
+#define E_EDITOR_ACTION_CONTEXT_PROPERTIES_CELL(editor) \
+	E_EDITOR_ACTION ((editor), "context-properties-cell")
+#define E_EDITOR_ACTION_CONTEXT_PROPERTIES_IMAGE(editor) \
+	E_EDITOR_ACTION ((editor), "context-properties-image")
+#define E_EDITOR_ACTION_CONTEXT_PROPERTIES_LINK(editor) \
+	E_EDITOR_ACTION ((editor), "context-properties-link")
+#define E_EDITOR_ACTION_CONTEXT_PROPERTIES_PARAGRAPH(editor) \
+	E_EDITOR_ACTION ((editor), "context-properties-paragraph")
+#define E_EDITOR_ACTION_CONTEXT_PROPERTIES_RULE(editor) \
+	E_EDITOR_ACTION ((editor), "context-properties-rule")
+#define E_EDITOR_ACTION_CONTEXT_PROPERTIES_TABLE(editor) \
+	E_EDITOR_ACTION ((editor), "context-properties-table")
+#define E_EDITOR_ACTION_CONTEXT_PROPERTIES_TEXT(editor) \
+	E_EDITOR_ACTION ((editor), "context-properties-text")
+#define E_EDITOR_ACTION_CONTEXT_REMOVE_LINK(editor) \
+	E_EDITOR_ACTION ((editor), "context-remove-link")
+#define E_EDITOR_ACTION_CONTEXT_SPELL_ADD(editor) \
+	E_EDITOR_ACTION ((editor), "context-spell-add")
+#define E_EDITOR_ACTION_CONTEXT_SPELL_ADD_MENU(editor) \
+	E_EDITOR_ACTION ((editor), "context-spell-add-menu")
+#define E_EDITOR_ACTION_CONTEXT_SPELL_IGNORE(editor) \
+	E_EDITOR_ACTION ((editor), "context-spell-ignore")
+#define E_EDITOR_ACTION_COPY(editor) \
+	E_EDITOR_ACTION ((editor), "copy")
+#define E_EDITOR_ACTION_CUT(editor) \
+	E_EDITOR_ACTION ((editor), "cut")
+#define E_EDITOR_ACTION_EDIT_MENU(editor) \
+	E_EDITOR_ACTION ((editor), "edit-menu")
+#define E_EDITOR_ACTION_FIND(editor) \
+	E_EDITOR_ACTION ((editor), "find")
+#define E_EDITOR_ACTION_FIND_AND_REPLACE(editor) \
+	E_EDITOR_ACTION ((editor), "find-and-replace")
+#define E_EDITOR_ACTION_FORMAT_MENU(editor) \
+	E_EDITOR_ACTION ((editor), "format-menu")
+#define E_EDITOR_ACTION_FORMAT_TEXT(editor) \
+	E_EDITOR_ACTION ((editor), "format-text")
+#define E_EDITOR_ACTION_INSERT_IMAGE(editor) \
+	E_EDITOR_ACTION ((editor), "insert-image")
+#define E_EDITOR_ACTION_INSERT_LINK(editor) \
+	E_EDITOR_ACTION ((editor), "insert-link")
+#define E_EDITOR_ACTION_INSERT_MENU(editor) \
+	E_EDITOR_ACTION ((editor), "insert-menu")
+#define E_EDITOR_ACTION_INSERT_RULE(editor) \
+	E_EDITOR_ACTION ((editor), "insert-rule")
+#define E_EDITOR_ACTION_INSERT_TABLE(editor) \
+	E_EDITOR_ACTION ((editor), "insert-table")
+#define E_EDITOR_ACTION_ITALIC(editor) \
+	E_EDITOR_ACTION ((editor), "italic")
+#define E_EDITOR_ACTION_JUSTIFY_CENTER(editor) \
+	E_EDITOR_ACTION ((editor), "justify-center")
+#define E_EDITOR_ACTION_JUSTIFY_LEFT(editor) \
+	E_EDITOR_ACTION ((editor), "justify-left")
+#define E_EDITOR_ACTION_JUSTIFY_RIGHT(editor) \
+	E_EDITOR_ACTION ((editor), "justify-right")
+#define E_EDITOR_ACTION_MODE_HTML(editor) \
+	E_EDITOR_ACTION ((editor), "mode-html")
+#define E_EDITOR_ACTION_MODE_PLAIN(editor) \
+	E_EDITOR_ACTION ((editor), "mode-plain")
+#define E_EDITOR_ACTION_MONOSPACED(editor) \
+	E_EDITOR_ACTION ((editor), "monospaced")
+#define E_EDITOR_ACTION_PROPERTIES_RULE(editor) \
+	E_EDITOR_ACTION ((editor), "properties-rule")
+#define E_EDITOR_ACTION_PROPERTIES_TABLE(editor) \
+	E_EDITOR_ACTION ((editor), "properties-table")
+#define E_EDITOR_ACTION_SHOW_FIND(editor) \
+	E_EDITOR_ACTION ((editor), "show-find")
+#define E_EDITOR_ACTION_SHOW_REPLACE(editor) \
+	E_EDITOR_ACTION ((editor), "show-replace")
+#define E_EDITOR_ACTION_SIZE_PLUS_ZERO(editor) \
+	E_EDITOR_ACTION ((editor), "size-plus-zero")
+#define E_EDITOR_ACTION_SPELL_CHECK(editor) \
+	E_EDITOR_ACTION ((editor), "spell-check")
+#define E_EDITOR_ACTION_STRIKETHROUGH(editor) \
+	E_EDITOR_ACTION ((editor), "strikethrough")
+#define E_EDITOR_ACTION_STYLE_ADDRESS(editor) \
+	E_EDITOR_ACTION ((editor), "style-address")
+#define E_EDITOR_ACTION_STYLE_H1(editor) \
+	E_EDITOR_ACTION ((editor), "style-h1")
+#define E_EDITOR_ACTION_STYLE_H2(editor) \
+	E_EDITOR_ACTION ((editor), "style-h2")
+#define E_EDITOR_ACTION_STYLE_H3(editor) \
+	E_EDITOR_ACTION ((editor), "style-h3")
+#define E_EDITOR_ACTION_STYLE_H4(editor) \
+	E_EDITOR_ACTION ((editor), "style-h4")
+#define E_EDITOR_ACTION_STYLE_H5(editor) \
+	E_EDITOR_ACTION ((editor), "style-h5")
+#define E_EDITOR_ACTION_STYLE_H6(editor) \
+	E_EDITOR_ACTION ((editor), "style-h6")
+#define E_EDITOR_ACTION_STYLE_NORMAL(editor) \
+	E_EDITOR_ACTION ((editor), "style-normal")
+#define E_EDITOR_ACTION_TEST_URL(editor) \
+	E_EDITOR_ACTION ((editor), "test-url")
+#define E_EDITOR_ACTION_UNDERLINE(editor) \
+	E_EDITOR_ACTION ((editor), "underline")
+#define E_EDITOR_ACTION_UNINDENT(editor) \
+	E_EDITOR_ACTION ((editor), "unindent")
+
+#endif /* E_EDITOR_ACTIONS_H */
diff --git a/e-util/e-editor-private.h b/e-util/e-editor-private.h
new file mode 100644
index 0000000..31451f0
--- /dev/null
+++ b/e-util/e-editor-private.h
@@ -0,0 +1,72 @@
+/* e-editor-private.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef E_EDITOR_PRIVATE_H
+#define E_EDITOR_PRIVATE_H
+
+#include <e-editor.h>
+#include <e-action-combo-box.h>
+#include <e-color-combo.h>
+#include <e-editor-actions.h>
+#include <e-editor-widgets.h>
+#include <e-editor-widget.h>
+
+#ifdef HAVE_XFREE
+#include <X11/XF86keysym.h>
+#endif
+
+
+#define ACTION(name) (E_EDITOR_ACTION_##name (editor))
+#define WIDGET(name) (E_EDITOR_WIDGETS_##name (editor))
+
+G_BEGIN_DECLS
+
+struct _EEditorPrivate {
+	GtkUIManager *manager;
+	GtkActionGroup *core_actions;
+	GtkActionGroup *html_actions;
+	GtkActionGroup *context_actions;
+	GtkActionGroup *html_context_actions;
+	GtkActionGroup *language_actions;
+	GtkActionGroup *spell_check_actions;
+	GtkActionGroup *suggestion_actions;
+	GtkBuilder *builder;
+
+	GtkWidget *main_menu;
+	GtkWidget *main_toolbar;
+	GtkWidget *edit_toolbar;
+	GtkWidget *html_toolbar;
+	GtkWidget *edit_area;
+
+	GtkWidget *color_combo_box;
+	GtkWidget *mode_combo_box;
+	GtkWidget *size_combo_box;
+	GtkWidget *style_combo_box;
+	GtkWidget *scrolled_window;
+
+	EEditorWidget *editor_widget;
+
+	guint ignore_style_change : 1;
+
+	gchar *filename;
+};
+
+void		editor_actions_init		(EEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_EDITOR_PRIVATE_H */
diff --git a/e-util/e-editor-widgets.h b/e-util/e-editor-widgets.h
new file mode 100644
index 0000000..5b28a7e
--- /dev/null
+++ b/e-util/e-editor-widgets.h
@@ -0,0 +1,234 @@
+/* e-editor-widgets.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EDITOR_WIDGETS_H
+#define E_EDITOR_WIDGETS_H
+
+#define E_EDITOR_WIDGETS(editor, name) \
+	(e_editor_get_widget ((editor), (name)))
+
+/* Cell Properties Window */
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_CELL_RADIO_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-cell-radio-button")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_COLOR_COMBO(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-color-combo")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_COLUMN_RADIO_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-column-radio-button")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_COLUMN_SPAN_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-column-span-spin-button")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_HEADER_STYLE_CHECK_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-header-style-check-button")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_HORIZONTAL_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-horizontal-combo-box")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_IMAGE_FILE_CHOOSER(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-image-file-chooser")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_ROW_RADIO_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-row-radio-button")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_ROW_SPAN_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-row-span-spin-button")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_TABLE_RADIO_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-table-radio-button")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_VERTICAL_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-vertical-combo-box")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_WIDTH_CHECK_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-width-check-button")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_WIDTH_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-width-combo-box")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_WIDTH_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-width-spin-button")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-window")
+#define E_EDITOR_WIDGETS_CELL_PROPERTIES_WRAP_TEXT_CHECK_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "cell-properties-wrap-text-check-button")
+
+/* Find Window */
+#define E_EDITOR_WIDGETS_FIND_BACKWARDS(editor) \
+	E_EDITOR_WIDGETS ((editor), "find-backwards")
+#define E_EDITOR_WIDGETS_FIND_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "find-button")
+#define E_EDITOR_WIDGETS_FIND_CASE_SENSITIVE(editor) \
+	E_EDITOR_WIDGETS ((editor), "find-case-sensitive")
+#define E_EDITOR_WIDGETS_FIND_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "find-window")
+#define E_EDITOR_WIDGETS_FIND_ENTRY(editor) \
+	E_EDITOR_WIDGETS ((editor), "find-entry")
+#define E_EDITOR_WIDGETS_FIND_REGULAR_EXPRESSION(editor) \
+	E_EDITOR_WIDGETS ((editor), "find-regular-expression")
+#define E_EDITOR_WIDGETS_FIND_RESULT_LABEL(editor) \
+	E_EDITOR_WIDGETS ((editor), "find-result-label")
+#define E_EDITOR_WIDGETS_FIND_WRAP(editor) \
+	E_EDITOR_WIDGETS ((editor), "find-wrap")
+
+/* Image Properties Window */
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_ALIGNMENT_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-alignment-combo-box")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_BORDER_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-border-spin-button")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_DESCRIPTION_ENTRY(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-description-entry")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_HEIGHT_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-height-combo-box")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_HEIGHT_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-height-spin-button")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_SOURCE_FILE_CHOOSER(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-source-file-chooser")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_URL_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-url-button")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_URL_ENTRY(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-url-entry")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_WIDTH_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-width-combo-box")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_WIDTH_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-width-spin-button")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-window")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_X_PADDING_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-x-padding-spin-button")
+#define E_EDITOR_WIDGETS_IMAGE_PROPERTIES_Y_PADDING_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "image-properties-y-padding-spin-button")
+
+/* Link Properties Window */
+#define E_EDITOR_WIDGETS_LINK_PROPERTIES_DESCRIPTION_ENTRY(editor) \
+	E_EDITOR_WIDGETS ((editor), "link-properties-description-entry")
+#define E_EDITOR_WIDGETS_LINK_PROPERTIES_TEST_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "link-properties-test-button")
+#define E_EDITOR_WIDGETS_LINK_PROPERTIES_URL_ENTRY(editor) \
+	E_EDITOR_WIDGETS ((editor), "link-properties-url-entry")
+#define E_EDITOR_WIDGETS_LINK_PROPERTIES_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "link-properties-window")
+
+/* Page Properties Window */
+#define E_EDITOR_WIDGETS_PAGE_PROPERTIES_BACKGROUND_COLOR_COMBO(editor) \
+	E_EDITOR_WIDGETS ((editor), "page-properties-background-color-combo")
+#define E_EDITOR_WIDGETS_PAGE_PROPERTIES_CUSTOM_FILE_CHOOSER(editor) \
+	E_EDITOR_WIDGETS ((editor), "page-properties-custom-file-chooser")
+#define E_EDITOR_WIDGETS_PAGE_PROPERTIES_LINK_COLOR_COMBO(editor) \
+	E_EDITOR_WIDGETS ((editor), "page-properties-link-color-combo")
+#define E_EDITOR_WIDGETS_PAGE_PROPERTIES_TEMPLATE_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "page-properties-template-combo-box")
+#define E_EDITOR_WIDGETS_PAGE_PROPERTIES_TEXT_COLOR_COMBO(editor) \
+	E_EDITOR_WIDGETS ((editor), "page-properties-text-color-combo")
+#define E_EDITOR_WIDGETS_PAGE_PROPERTIES_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "page-properties-window")
+
+/* Paragraph Properties Window */
+#define E_EDITOR_WIDGETS_PARAGRAPH_PROPERTIES_CENTER_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "paragraph-properties-center-button")
+#define E_EDITOR_WIDGETS_PARAGRAPH_PROPERTIES_LEFT_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "paragraph-properties-left-button")
+#define E_EDITOR_WIDGETS_PARAGRAPH_PROPERTIES_RIGHT_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "paragraph-properties-right-button")
+#define E_EDITOR_WIDGETS_PARAGRAPH_PROPERTIES_STYLE_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "paragraph-properties-style-combo-box")
+#define E_EDITOR_WIDGETS_PARAGRAPH_PROPERTIES_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "paragraph-properties-window")
+
+/* Replace Confirmation Window */
+#define E_EDITOR_WIDGETS_REPLACE_CONFIRMATION_CLOSE_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-confirmation-close-button")
+#define E_EDITOR_WIDGETS_REPLACE_CONFIRMATION_NEXT_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-confirmation-next-button")
+#define E_EDITOR_WIDGETS_REPLACE_CONFIRMATION_REPLACE_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-confirmation-replace-button")
+#define E_EDITOR_WIDGETS_REPLACE_CONFIRMATION_REPLACE_ALL_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-confirmation-replace-all-button")
+#define E_EDITOR_WIDGETS_REPLACE_CONFIRMATION_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-confirmation-window")
+
+/* Replace Window */
+#define E_EDITOR_WIDGETS_REPLACE_BACKWARDS(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-backwards")
+#define E_EDITOR_WIDGETS_REPLACE_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-button")
+#define E_EDITOR_WIDGETS_REPLACE_CASE_SENSITIVE(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-case-sensitive")
+#define E_EDITOR_WIDGETS_REPLACE_ENTRY(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-entry")
+#define E_EDITOR_WIDGETS_REPLACE_WITH_ENTRY(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-with-entry")
+#define E_EDITOR_WIDGETS_REPLACE_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-window")
+#define E_EDITOR_WIDGETS_REPLACE_WRAP(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-wrap")
+#define E_EDITOR_WIDGETS_REPLACE_ONLY_SELECTION(editor) \
+	E_EDITOR_WIDGETS ((editor), "replace-only-selection")
+
+/* Rule Properties Window */
+#define E_EDITOR_WIDGETS_RULE_PROPERTIES_ALIGNMENT_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "rule-properties-alignment-combo-box")
+#define E_EDITOR_WIDGETS_RULE_PROPERTIES_SHADED_CHECK_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "rule-properties-shaded-check-button")
+#define E_EDITOR_WIDGETS_RULE_PROPERTIES_SIZE_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "rule-properties-size-spin-button")
+#define E_EDITOR_WIDGETS_RULE_PROPERTIES_WIDTH_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "rule-properties-width-combo-box")
+#define E_EDITOR_WIDGETS_RULE_PROPERTIES_WIDTH_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "rule-properties-width-spin-button")
+#define E_EDITOR_WIDGETS_RULE_PROPERTIES_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "rule-properties-window")
+
+/* Spell Check Window */
+#define E_EDITOR_WIDGETS_SPELL_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "spell-window")
+
+/* Table Properties Window */
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_ALIGNMENT_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-alignment-combo-box")
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_BORDER_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-border-spin-button")
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_COLOR_COMBO(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-color-combo")
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_COLS_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-cols-spin-button")
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_IMAGE_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-image-button")
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_PADDING_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-padding-spin-button")
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_ROWS_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-rows-spin-button")
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_SPACING_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-spacing-spin-button")
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_WIDTH_CHECK_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-width-check-button")
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_WIDTH_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-width-combo-box")
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_WIDTH_SPIN_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-width-spin-button")
+#define E_EDITOR_WIDGETS_TABLE_PROPERTIES_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "table-properties-window")
+
+/* Text Properties Window */
+#define E_EDITOR_WIDGETS_TEXT_PROPERTIES_BOLD_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "text-properties-bold-button")
+#define E_EDITOR_WIDGETS_TEXT_PROPERTIES_COLOR_COMBO(editor) \
+	E_EDITOR_WIDGETS ((editor), "text-properties-color-combo")
+#define E_EDITOR_WIDGETS_TEXT_PROPERTIES_ITALIC_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "text-properties-italic-button")
+#define E_EDITOR_WIDGETS_TEXT_PROPERTIES_SIZE_COMBO_BOX(editor) \
+	E_EDITOR_WIDGETS ((editor), "text-properties-size-combo-box")
+#define E_EDITOR_WIDGETS_TEXT_PROPERTIES_STRIKETHROUGH_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "text-properties-strikethrough-button")
+#define E_EDITOR_WIDGETS_TEXT_PROPERTIES_UNDERLINE_BUTTON(editor) \
+	E_EDITOR_WIDGETS ((editor), "text-properties-underline-button")
+#define E_EDITOR_WIDGETS_TEXT_PROPERTIES_WINDOW(editor) \
+	E_EDITOR_WIDGETS ((editor), "text-properties-window")
+
+#endif /* E_EDITOR_WIDGETS_H */
diff --git a/e-util/e-editor.c b/e-util/e-editor.c
new file mode 100644
index 0000000..61bfdd9
--- /dev/null
+++ b/e-util/e-editor.c
@@ -0,0 +1,576 @@
+/* e-editor.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-editor.h"
+#include "e-editor-private.h"
+
+#include <glib/gi18n-lib.h>
+
+G_DEFINE_TYPE (
+	EEditor,
+	e_editor,
+	GTK_TYPE_BOX);
+
+enum {
+	PROP_0,
+	PROP_FILENAME
+};
+
+static gchar *
+editor_find_ui_file (const gchar *basename)
+{
+	gchar *filename;
+
+	g_return_val_if_fail (basename != NULL, NULL);
+
+	/* Support running directly from the source tree. */
+	filename = g_build_filename (".", basename, NULL);
+	if (g_file_test (filename, G_FILE_TEST_EXISTS))
+		return filename;
+	g_free (filename);
+
+	/* XXX This is kinda broken. */
+	filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);
+	if (g_file_test (filename, G_FILE_TEST_EXISTS))
+		return filename;
+	g_free (filename);
+
+	g_critical ("Could not locate '%s'", basename);
+
+	return NULL;
+}
+
+static void
+editor_set_property (GObject *object,
+		     guint property_id,
+		     const GValue *value,
+		     GParamSpec *pspec)
+{
+	switch (property_id) {
+
+		case PROP_FILENAME:
+			e_editor_set_filename (E_EDITOR (object),
+				g_value_get_string (value));
+			return;
+
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+editor_get_property (GObject *object,
+		     guint property_id,
+		     GValue *value,
+		     GParamSpec *pspec)
+{
+	switch (property_id) {
+
+		case PROP_FILENAME:
+			g_value_set_string (
+				value, e_editor_get_filename(
+				E_EDITOR (object)));
+		return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+editor_constructed (GObject *object)
+{
+	EEditor *editor = E_EDITOR (object);
+	EEditorPrivate *priv = editor->priv;
+
+	GtkWidget *widget;
+	GtkToolbar *toolbar;
+	GtkToolItem *tool_item;
+
+	/* Construct main window widgets. */
+
+	widget = e_editor_get_managed_widget (editor, "/main-menu");
+	gtk_box_pack_start (GTK_BOX (editor), widget, FALSE, FALSE, 0);
+	priv->main_menu = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = e_editor_get_managed_widget (editor, "/main-toolbar");
+	gtk_box_pack_start (GTK_BOX (editor), widget, FALSE, FALSE, 0);
+	priv->main_toolbar = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	gtk_style_context_add_class (
+		gtk_widget_get_style_context (widget),
+		GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
+
+	widget = e_editor_get_managed_widget (editor, "/edit-toolbar");
+	gtk_toolbar_set_style (GTK_TOOLBAR (widget), GTK_TOOLBAR_BOTH_HORIZ);
+	gtk_box_pack_start (GTK_BOX (editor), widget, FALSE, FALSE, 0);
+	priv->edit_toolbar = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = e_editor_get_managed_widget (editor, "/html-toolbar");
+	gtk_toolbar_set_style (GTK_TOOLBAR (widget), GTK_TOOLBAR_BOTH_HORIZ);
+	gtk_box_pack_start (GTK_BOX (editor), widget, FALSE, FALSE, 0);
+	priv->html_toolbar = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (
+		GTK_SCROLLED_WINDOW (widget),
+		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (
+		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+	gtk_box_pack_start (GTK_BOX (editor), widget, TRUE, TRUE, 0);
+	priv->scrolled_window = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = GTK_WIDGET (e_editor_get_editor_widget (editor));
+	gtk_container_add (GTK_CONTAINER (priv->scrolled_window), widget);
+	gtk_widget_show (widget);
+
+	/* Add some combo boxes to the "edit" toolbar. */
+
+	toolbar = GTK_TOOLBAR (priv->edit_toolbar);
+
+	tool_item = gtk_tool_item_new ();
+	widget = e_action_combo_box_new_with_action (
+		GTK_RADIO_ACTION (ACTION (STYLE_NORMAL)));
+	gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+	gtk_container_add (GTK_CONTAINER (tool_item), widget);
+	gtk_widget_set_tooltip_text (widget, _("Paragraph Style"));
+	gtk_toolbar_insert (toolbar, tool_item, 0);
+	priv->style_combo_box = g_object_ref (widget);
+	gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+	tool_item = gtk_separator_tool_item_new ();
+	gtk_toolbar_insert (toolbar, tool_item, 0);
+	gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+	tool_item = gtk_tool_item_new ();
+	widget = e_action_combo_box_new_with_action (
+		GTK_RADIO_ACTION (ACTION (MODE_HTML)));
+	gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+	gtk_container_add (GTK_CONTAINER (tool_item), widget);
+	gtk_widget_set_tooltip_text (widget, _("Editing Mode"));
+	gtk_toolbar_insert (toolbar, tool_item, 0);
+	priv->mode_combo_box = g_object_ref (widget);
+	gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+	/* Add some combo boxes to the "html" toolbar. */
+
+	toolbar = GTK_TOOLBAR (priv->html_toolbar);
+
+	tool_item = gtk_tool_item_new ();
+	widget = e_color_combo_new ();
+	gtk_container_add (GTK_CONTAINER (tool_item), widget);
+	gtk_widget_set_tooltip_text (widget, _("Font Color"));
+	gtk_toolbar_insert (toolbar, tool_item, 0);
+	priv->color_combo_box = g_object_ref (widget);
+	gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+	tool_item = gtk_tool_item_new ();
+	widget = e_action_combo_box_new_with_action (
+		GTK_RADIO_ACTION (ACTION (SIZE_PLUS_ZERO)));
+	gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+	gtk_container_add (GTK_CONTAINER (tool_item), widget);
+	gtk_widget_set_tooltip_text (widget, _("Font Size"));
+	gtk_toolbar_insert (toolbar, tool_item, 0);
+	priv->size_combo_box = g_object_ref (widget);
+	gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+	/* Initialize painters (requires "edit_area"). */
+
+	/* FIXME WEBKIT
+	html = e_editor_widget_get_html (E_EDITOR_WIDGET (editor));
+	gtk_widget_ensure_style (GTK_WIDGET (html));
+	priv->html_painter = g_object_ref (html->engine->painter);
+	priv->plain_painter = html_plain_painter_new (priv->edit_area, TRUE);
+	*/
+
+	/* Add input methods to the context menu. */
+
+	/* FIXME WEBKIT
+	widget = e_editor_get_managed_widget (
+		editor, "/context-menu/context-input-methods-menu");
+	widget = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+	gtk_im_multicontext_append_menuitems (
+		GTK_IM_MULTICONTEXT (html->priv->im_context),
+		GTK_MENU_SHELL (widget));
+	*/
+
+	/* Configure color stuff. */
+
+	/* FIXME WEBKIT
+	priv->palette = gtkhtml_color_palette_new ();
+	priv->text_color = gtkhtml_color_state_new ();
+
+	gtkhtml_color_state_set_default_label (
+		priv->text_color, _("Automatic"));
+	gtkhtml_color_state_set_palette (
+		priv->text_color, priv->palette);
+	*/
+
+	/* Text color widgets share state. */
+
+	/* FIXME WEBKIT
+	widget = priv->color_combo_box;
+	gtkhtml_color_combo_set_state (
+		GTKHTML_COLOR_COMBO (widget), priv->text_color);
+
+	widget = WIDGET (TEXT_PROPERTIES_COLOR_COMBO);
+	gtkhtml_color_combo_set_state (
+		GTKHTML_COLOR_COMBO (widget), priv->text_color);
+	*/
+
+	/* These color widgets share a custom color palette. */
+
+	/* FIXME WEBKIT
+	widget = WIDGET (CELL_PROPERTIES_COLOR_COMBO);
+	gtkhtml_color_combo_set_palette (
+		GTKHTML_COLOR_COMBO (widget), priv->palette);
+
+	widget = WIDGET (PAGE_PROPERTIES_BACKGROUND_COLOR_COMBO);
+	gtkhtml_color_combo_set_palette (
+		GTKHTML_COLOR_COMBO (widget), priv->palette);
+
+	widget = WIDGET (PAGE_PROPERTIES_LINK_COLOR_COMBO);
+	gtkhtml_color_combo_set_palette (
+		GTKHTML_COLOR_COMBO (widget), priv->palette);
+
+	widget = WIDGET (TABLE_PROPERTIES_COLOR_COMBO);
+	gtkhtml_color_combo_set_palette (
+		GTKHTML_COLOR_COMBO (widget), priv->palette);
+		*/
+}
+
+static void
+editor_dispose (GObject *object)
+{
+	EEditor *editor = E_EDITOR (object);
+	EEditorPrivate *priv = editor->priv;
+
+	/* Disconnect signal handlers from the color
+	 * state object since it may live on. */
+	/* FIXME WEBKIT
+	if (priv->text_color != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->text_color, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, editor);
+	}
+	*/
+
+	g_clear_object (&priv->manager);
+	g_clear_object (&priv->manager);
+	g_clear_object (&priv->core_actions);
+	g_clear_object (&priv->html_actions);
+	g_clear_object (&priv->context_actions);
+	g_clear_object (&priv->html_context_actions);
+	g_clear_object (&priv->language_actions);
+	g_clear_object (&priv->spell_check_actions);
+	g_clear_object (&priv->suggestion_actions);
+	g_clear_object (&priv->builder);
+
+	/* FIXME WEBKIT
+	g_hash_table_remove_all (priv->available_spell_checkers);
+
+	g_list_foreach (
+		priv->active_spell_checkers,
+		(GFunc) g_object_unref, NULL);
+	g_list_free (priv->active_spell_checkers);
+	priv->active_spell_checkers = NULL;
+	*/
+
+	g_clear_object (&priv->main_menu);
+	g_clear_object (&priv->main_toolbar);
+	g_clear_object (&priv->edit_toolbar);
+	g_clear_object (&priv->html_toolbar);
+	g_clear_object (&priv->edit_area);
+
+	g_clear_object (&priv->color_combo_box);
+	g_clear_object (&priv->mode_combo_box);
+	g_clear_object (&priv->size_combo_box);
+	g_clear_object (&priv->style_combo_box);
+	g_clear_object (&priv->scrolled_window);
+
+	/* FIXME WEBKIT
+	DISPOSE (priv->palette);
+	DISPOSE (priv->text_color);
+	*/
+}
+
+static void
+e_editor_class_init (EEditorClass *klass)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (klass, sizeof (EEditorPrivate));
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->set_property = editor_set_property;
+	object_class->get_property = editor_get_property;
+	object_class->constructed = editor_constructed;
+	object_class->dispose = editor_dispose;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_FILENAME,
+		g_param_spec_string (
+			"filename",
+		        NULL,
+		        NULL,
+		        NULL,
+		        G_PARAM_READWRITE));
+}
+
+static void
+e_editor_init (EEditor *editor)
+{
+	EEditorPrivate *priv;
+	GError *error;
+	gchar *filename;
+
+	editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+		editor, E_TYPE_EDITOR, EEditorPrivate);
+
+	priv = editor->priv;
+
+	priv->manager = gtk_ui_manager_new ();
+	priv->core_actions = gtk_action_group_new ("core");
+	priv->html_actions = gtk_action_group_new ("html");
+	priv->context_actions = gtk_action_group_new ("core-context");
+	priv->html_context_actions = gtk_action_group_new ("html-context");
+	priv->language_actions = gtk_action_group_new ("language");
+	priv->spell_check_actions = gtk_action_group_new ("spell-check");
+	priv->suggestion_actions = gtk_action_group_new ("suggestion");
+	priv->editor_widget = e_editor_widget_new ();
+
+	filename = editor_find_ui_file ("e-editor-manager.ui");
+
+	error = NULL;
+	if (!gtk_ui_manager_add_ui_from_file (priv->manager, filename, &error)) {
+		g_critical ("Couldn't load builder file: %s\n", error->message);
+		g_clear_error (&error);
+	}
+
+	g_free (filename);
+
+	filename = editor_find_ui_file ("e-editor-builder.ui");
+
+	priv->builder = gtk_builder_new ();
+	/* To keep translated strings in subclasses */
+	gtk_builder_set_translation_domain (priv->builder, GETTEXT_PACKAGE);
+	error = NULL;
+	if (!gtk_builder_add_from_file (priv->builder, filename, &error)) {
+		g_critical ("Couldn't load builder file: %s\n", error->message);
+		g_clear_error (&error);
+	}
+
+	g_free (filename);
+
+	editor_actions_init (editor);
+}
+
+GtkWidget *
+e_editor_new (void)
+{
+	return g_object_new (E_TYPE_EDITOR,
+		"orientation", GTK_ORIENTATION_VERTICAL,
+		NULL);
+}
+
+EEditorWidget *
+e_editor_get_editor_widget (EEditor *editor)
+{
+	g_return_val_if_fail (E_IS_EDITOR (editor), NULL);
+
+	return editor->priv->editor_widget;
+}
+
+
+GtkBuilder *
+e_editor_get_builder (EEditor *editor)
+{
+	g_return_val_if_fail (E_IS_EDITOR (editor), NULL);
+
+	return editor->priv->builder;
+}
+
+GtkUIManager *
+e_editor_get_ui_manager (EEditor *editor)
+{
+	g_return_val_if_fail (E_IS_EDITOR (editor), NULL);
+
+	return editor->priv->manager;
+}
+
+GtkAction *
+e_editor_get_action (EEditor *editor,
+		     const gchar *action_name)
+{
+	GtkUIManager *manager;
+	GtkAction *action = NULL;
+	GList *iter;
+
+	g_return_val_if_fail (E_IS_EDITOR (editor), NULL);
+	g_return_val_if_fail (action_name != NULL, NULL);
+
+	manager = e_editor_get_ui_manager (editor);
+	iter = gtk_ui_manager_get_action_groups (manager);
+
+	while (iter != NULL && action == NULL) {
+		GtkActionGroup *action_group = iter->data;
+
+		action = gtk_action_group_get_action (
+			action_group, action_name);
+		iter = g_list_next (iter);
+	}
+
+	g_return_val_if_fail (action != NULL, NULL);
+
+	return action;
+}
+
+GtkActionGroup *
+e_editor_get_action_group (EEditor *editor,
+			   const gchar *group_name)
+{
+	GtkUIManager *manager;
+	GList *iter;
+
+	g_return_val_if_fail (E_IS_EDITOR (editor), NULL);
+	g_return_val_if_fail (group_name != NULL, NULL);
+
+	manager = e_editor_get_ui_manager (editor);
+	iter = gtk_ui_manager_get_action_groups (manager);
+
+	while (iter != NULL) {
+		GtkActionGroup *action_group = iter->data;
+		const gchar *name;
+
+		name = gtk_action_group_get_name (action_group);
+		if (strcmp (name, group_name) == 0)
+			return action_group;
+
+		iter = g_list_next (iter);
+	}
+
+	return NULL;
+}
+
+GtkWidget *
+e_editor_get_widget (EEditor *editor,
+                           const gchar *widget_name)
+{
+	GtkBuilder *builder;
+	GObject *object;
+
+	g_return_val_if_fail (E_IS_EDITOR (editor), NULL);
+	g_return_val_if_fail (widget_name != NULL, NULL);
+
+	builder = e_editor_get_builder (editor);
+	object = gtk_builder_get_object (builder, widget_name);
+	g_return_val_if_fail (GTK_IS_WIDGET (object), NULL);
+
+	return GTK_WIDGET (object);
+}
+
+GtkWidget *
+e_editor_get_managed_widget (EEditor *editor,
+			     const gchar *widget_path)
+{
+	GtkUIManager *manager;
+	GtkWidget *widget;
+
+	g_return_val_if_fail (E_IS_EDITOR (editor), NULL);
+	g_return_val_if_fail (widget_path != NULL, NULL);
+
+	manager = e_editor_get_ui_manager (editor);
+	widget = gtk_ui_manager_get_widget (manager, widget_path);
+
+	g_return_val_if_fail (widget != NULL, NULL);
+
+	return widget;
+}
+
+const gchar *
+e_editor_get_filename (EEditor *editor)
+{
+	g_return_val_if_fail (E_IS_EDITOR (editor), NULL);
+
+	return editor->priv->filename;
+}
+
+void
+e_editor_set_filename (EEditor *editor,
+		       const gchar *filename)
+{
+	g_return_if_fail (E_IS_EDITOR (editor));
+
+	if (g_strcmp0 (editor->priv->filename, filename) == 0)
+		return;
+
+	g_free (editor->priv->filename);
+	editor->priv->filename = g_strdup (filename);
+
+	g_object_notify (G_OBJECT (editor), "filename");
+}
+
+gboolean
+e_editor_save (EEditor *editor,
+	       const gchar *filename,
+	       gboolean as_html,
+	       GError **error)
+{
+	GFile *file;
+	GFileOutputStream *stream;
+	gchar *content;
+	gsize written;
+
+	file = g_file_new_for_path (filename);
+	stream = g_file_replace (
+		file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error);
+	if ((error && *error) || !stream)
+		return FALSE;
+
+	if (as_html) {
+		content = e_editor_widget_get_text_html (
+				E_EDITOR_WIDGET (editor));
+	} else {
+		content = e_editor_widget_get_text_plain (
+				E_EDITOR_WIDGET (editor));
+	}
+
+	if (!content || !*content) {
+		g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+			     "Failed to obtain content of editor");
+		return FALSE;
+	}
+
+	g_output_stream_write_all (
+		G_OUTPUT_STREAM (stream), content, strlen (content),
+		&written, NULL, error);
+
+	g_free (content);
+	g_object_unref (stream);
+	g_object_unref (file);
+
+	return TRUE;
+}
diff --git a/e-util/e-editor.h b/e-util/e-editor.h
new file mode 100644
index 0000000..9b878eb
--- /dev/null
+++ b/e-util/e-editor.h
@@ -0,0 +1,95 @@
+/* e-editor.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EDITOR_H
+#define E_EDITOR_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-editor-widget.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EDITOR \
+	(e_editor_get_type ())
+#define E_EDITOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_EDITOR, EEditor))
+#define E_EDITOR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_EDITOR, EEditorClass))
+#define E_IS_EDITOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_EDITOR))
+#define E_IS_EDITOR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_EDITOR))
+#define E_EDITOR_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_EDITOR, EEditorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEditor EEditor;
+typedef struct _EEditorClass EEditorClass;
+typedef struct _EEditorPrivate EEditorPrivate;
+
+struct _EEditor {
+	GtkBox parent;
+
+	EEditorPrivate *priv;
+};
+
+struct _EEditorClass {
+	GtkBoxClass parent_class;
+};
+
+GType		e_editor_get_type		(void);
+GtkWidget *	e_editor_new			(void);
+
+EEditorWidget*	e_editor_get_editor_widget	(EEditor *editor);
+
+GtkBuilder *	e_editor_get_builder		(EEditor *editor);
+GtkUIManager *	e_editor_get_ui_manager		(EEditor *editor);
+GtkAction *	e_editor_get_action		(EEditor *editor,
+						 const gchar *action_name);
+GtkActionGroup *e_editor_get_action_group	(EEditor *editor,
+						 const gchar *group_name);
+GtkWidget *	e_editor_get_widget		(EEditor *editor,
+						 const gchar *widget_name);
+GtkWidget *	e_editor_get_managed_widget
+						(EEditor *editor,
+						 const gchar *widget_path);
+const gchar *	e_editor_get_filename		(EEditor *editor);
+void		e_editor_set_filename		(EEditor *editor,
+						 const gchar *filename);
+
+
+/*****************************************************************************
+ * High-Level Editing Interface
+ *****************************************************************************/
+
+gboolean	e_editor_save			(EEditor *editor,
+						 const gchar *filename,
+						 gboolean as_html,
+						 GError **error);
+
+G_END_DECLS
+
+#endif /* E_EDITOR_H */
diff --git a/e-util/e-util.h b/e-util/e-util.h
index 95c0ead..25cee07 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -90,8 +90,11 @@
 #include <e-util/e-destination-store.h>
 #include <e-util/e-dialog-utils.h>
 #include <e-util/e-dialog-widgets.h>
+#include <e-util/e-editor-actions.h>
 #include <e-util/e-editor-selection.h>
 #include <e-util/e-editor-widget.h>
+#include <e-util/e-editor-widgets.h>
+#include <e-util/e-editor.h>
 #include <e-util/e-emoticon-action.h>
 #include <e-util/e-emoticon-chooser-menu.h>
 #include <e-util/e-emoticon-chooser.h>



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