[evolution] Finish killing Bonobo.



commit fa9051e04051156a9e11e2af72a0d7342f4ea2e4
Author: Matthew Barnes <mbarnes redhat com>
Date:   Sun Sep 6 19:23:57 2009 -0400

    Finish killing Bonobo.

 Makefile.am                                   |    2 +-
 addressbook/gui/merging/Makefile.am           |    1 +
 addressbook/gui/widgets/Makefile.am           |    2 +-
 addressbook/gui/widgets/eab-contact-display.c |  157 ++--
 addressbook/gui/widgets/eab-contact-display.h |    6 +-
 calendar/Makefile.am                          |    2 +-
 calendar/gui/Makefile.am                      |    4 +-
 calendar/gui/alarm-notify/Makefile.am         |   31 +-
 calendar/gui/e-cal-component-preview.c        |   84 +--
 calendar/gui/e-cal-component-preview.h        |    7 +-
 calendar/idl/Makefile.am                      |    7 -
 calendar/idl/evolution-calendar.idl           |   52 --
 configure.ac                                  |   16 +-
 e-util/e-marshal.list                         |    2 +-
 e-util/e-plugin.c                             |  112 +--
 e-util/e-util.c                               |   41 +
 e-util/e-util.h                               |    5 +
 evolution-shell.pc.in                         |    2 -
 mail/e-mail-browser.c                         |   19 +-
 mail/e-mail-display.c                         |  753 +----------------
 mail/e-mail-display.h                         |   36 +-
 mail/e-mail-reader.c                          |   80 ++-
 mail/e-mail-search-bar.c                      |   96 ++-
 mail/e-mail-search-bar.h                      |    6 +-
 mail/em-utils.c                               |   92 --
 mail/em-utils.h                               |    3 -
 modules/addressbook/e-book-shell-backend.c    |   29 +
 modules/calendar/e-memo-shell-content.c       |   11 +-
 modules/calendar/e-memo-shell-view-actions.c  |    2 +-
 modules/calendar/e-memo-shell-view-private.c  |   25 -
 modules/calendar/e-memo-shell-view.c          |    2 +-
 modules/calendar/e-task-shell-content.c       |   11 +-
 modules/calendar/e-task-shell-view-actions.c  |    2 +-
 modules/calendar/e-task-shell-view-private.c  |   29 +-
 modules/calendar/e-task-shell-view.c          |    2 +-
 modules/mail/e-mail-shell-content.c           |   10 +-
 modules/mail/e-mail-shell-view-actions.c      |    4 +-
 modules/mail/e-mail-shell-view-private.h      |    1 +
 plugins/vcard-inline/Makefile.am              |    5 +-
 po/POTFILES.in                                |    3 +-
 shell/e-shell-view.c                          |   10 +-
 shell/e-shell-window-private.c                |   16 +-
 shell/e-shell.c                               |   34 +-
 widgets/misc/Makefile.am                      |    2 +
 widgets/misc/e-attachment-view.c              |   44 +-
 widgets/misc/e-signature-preview.c            |   71 +--
 widgets/misc/e-signature-preview.h            |    6 +-
 widgets/misc/e-web-view.c                     | 1110 +++++++++++++++++++++++++
 widgets/misc/e-web-view.h                     |  119 +++
 49 files changed, 1708 insertions(+), 1458 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index f79d67f..53b463b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -53,8 +53,8 @@ SUBDIRS = 			\
 	shell			\
 	$(SMIME_SUBDIR)		\
 	em-format		\
-	composer		\
 	addressbook		\
+	composer		\
 	mail			\
 	calendar		\
 	art			\
diff --git a/addressbook/gui/merging/Makefile.am b/addressbook/gui/merging/Makefile.am
index fd13c97..e399c19 100644
--- a/addressbook/gui/merging/Makefile.am
+++ b/addressbook/gui/merging/Makefile.am
@@ -2,6 +2,7 @@ AM_CPPFLAGS =						\
 	-DG_LOG_DOMAIN=\"eab-contact-merging\"		\
 	-DEVOLUTION_GLADEDIR=\""$(gladedir)"\"		\
 	-I$(top_srcdir)					\
+	-I$(top_srcdir)/widgets				\
 	-I$(top_srcdir)/addressbook			\
 	$(EVOLUTION_ADDRESSBOOK_CFLAGS)
 
diff --git a/addressbook/gui/widgets/Makefile.am b/addressbook/gui/widgets/Makefile.am
index 58d1e1b..b642fde 100644
--- a/addressbook/gui/widgets/Makefile.am
+++ b/addressbook/gui/widgets/Makefile.am
@@ -1,7 +1,7 @@
 ruledir = $(privdatadir)
 rule_DATA = addresstypes.xml
 
-AM_CPPFLAGS =										\
+AM_CPPFLAGS =						\
 	-DG_LOG_DOMAIN=\"eab-widgets\"			\
 	-DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\"	\
 	-DEVOLUTION_GALVIEWSDIR=\""$(viewsdir)"\"	\
diff --git a/addressbook/gui/widgets/eab-contact-display.c b/addressbook/gui/widgets/eab-contact-display.c
index d47b3e1..0ade284 100644
--- a/addressbook/gui/widgets/eab-contact-display.c
+++ b/addressbook/gui/widgets/eab-contact-display.c
@@ -240,71 +240,6 @@ contact_display_selection_clear_event (EABContactDisplay *display,
 }
 
 static void
-contact_display_on_url_requested (GtkHTML *html,
-                                  const gchar *url,
-                                  GtkHTMLStream *handle,
-                                  EABContactDisplay *display)
-{
-	if (!strcmp (url, "internal-contact-photo:")) {
-		EContactPhoto *photo;
-
-		photo = e_contact_get (display->priv->contact, E_CONTACT_PHOTO);
-		if (!photo)
-			photo = e_contact_get (display->priv->contact, E_CONTACT_LOGO);
-
-		gtk_html_stream_write (handle, (gchar *)photo->data.inlined.data, photo->data.inlined.length);
-
-		gtk_html_end (html, handle, GTK_HTML_STREAM_OK);
-
-		e_contact_photo_free (photo);
-	}
-	else if (!strncmp (url, "evo-icon:", strlen ("evo-icon:"))) {
-		gchar *data;
-		gsize data_length;
-		gchar *filename;
-
-		filename = e_icon_factory_get_icon_filename (url + strlen ("evo-icon:"), GTK_ICON_SIZE_MENU);
-		if (g_file_get_contents (filename, &data, &data_length, NULL)) {
-			gtk_html_stream_write (handle, data, data_length);
-			g_free (data);
-		}
-
-		gtk_html_stream_close (handle, GTK_HTML_STREAM_OK);
-
-		g_free (filename);
-	}
-}
-
-static void
-contact_display_on_link_clicked (GtkHTML *html,
-                                 const gchar *uri,
-                                 EABContactDisplay *display)
-{
-#ifdef HANDLE_MAILTO_INTERNALLY
-	if (!strncmp (uri, "internal-mailto:";, strlen ("internal-mailto:";))) {
-		EDestination *destination;
-		EContact *contact;
-		gint email_num;
-
-		email_num = atoi (uri + strlen ("internal-mailto:";));
-		if (email_num == -1)
-			return;
-
-		destination = e_destination_new ();
-		contact = eab_contact_display_get_contact (display);
-		e_destination_set_contact (destination, contact, email_num);
-		g_signal_emit (display, signals[SEND_MESSAGE], 0, destination);
-		g_object_unref (destination);
-
-		return;
-	}
-#endif
-
-	/* FIXME Pass a parent window. */
-	e_show_uri (NULL, uri);
-}
-
-static void
 render_name_value (GtkHTMLStream *html_stream, const gchar *label, const gchar *str, const gchar *icon, guint html_flags)
 {
 	gchar *value = e_text_to_html (str, html_flags);
@@ -1033,9 +968,89 @@ contact_display_finalize (GObject *object)
 }
 
 static void
+contact_display_url_requested (GtkHTML *html,
+                               const gchar *url,
+                               GtkHTMLStream *handle)
+{
+	EABContactDisplayPrivate *priv;
+
+	priv = EAB_CONTACT_DISPLAY_GET_PRIVATE (html);
+
+	if (strcmp (url, "internal-contact-photo:") == 0) {
+		EContactPhoto *photo;
+
+		photo = e_contact_get (priv->contact, E_CONTACT_PHOTO);
+		if (!photo)
+			photo = e_contact_get (priv->contact, E_CONTACT_LOGO);
+
+		gtk_html_stream_write (handle, (gchar *)photo->data.inlined.data, photo->data.inlined.length);
+
+		gtk_html_end (html, handle, GTK_HTML_STREAM_OK);
+
+		e_contact_photo_free (photo);
+
+		return;
+	}
+
+	if (strncmp (url, "evo-icon:", strlen ("evo-icon:")) == 0) {
+		gchar *data;
+		gsize data_length;
+		gchar *filename;
+
+		filename = e_icon_factory_get_icon_filename (url + strlen ("evo-icon:"), GTK_ICON_SIZE_MENU);
+		if (g_file_get_contents (filename, &data, &data_length, NULL)) {
+			gtk_html_stream_write (handle, data, data_length);
+			g_free (data);
+		}
+
+		gtk_html_stream_close (handle, GTK_HTML_STREAM_OK);
+
+		g_free (filename);
+
+		return;
+	}
+
+	/* Chain up to parent's url_requested() method. */
+	GTK_HTML_CLASS (parent_class)->url_requested (html, url, handle);
+}
+
+static void
+contact_display_link_clicked (GtkHTML *html,
+                              const gchar *uri)
+{
+	EABContactDisplay *display;
+
+	display = EAB_CONTACT_DISPLAY (html);
+
+#ifdef HANDLE_MAILTO_INTERNALLY
+	if (!strncmp (uri, "internal-mailto:";, strlen ("internal-mailto:";))) {
+		EDestination *destination;
+		EContact *contact;
+		gint email_num;
+
+		email_num = atoi (uri + strlen ("internal-mailto:";));
+		if (email_num == -1)
+			return;
+
+		destination = e_destination_new ();
+		contact = eab_contact_display_get_contact (display);
+		e_destination_set_contact (destination, contact, email_num);
+		g_signal_emit (display, signals[SEND_MESSAGE], 0, destination);
+		g_object_unref (destination);
+
+		return;
+	}
+#endif
+
+	/* Chain up to parent's link_clicked() method. */
+	GTK_HTML_CLASS (parent_class)->link_clicked (html, uri);
+}
+
+static void
 eab_contact_display_class_init (EABContactDisplayClass *class)
 {
 	GObjectClass *object_class;
+	GtkHTMLClass *html_class;
 
 	parent_class = g_type_class_peek_parent (class);
 	g_type_class_add_private (class, sizeof (EABContactDisplayPrivate));
@@ -1046,6 +1061,10 @@ eab_contact_display_class_init (EABContactDisplayClass *class)
 	object_class->dispose = contact_display_dispose;
 	object_class->finalize = contact_display_finalize;
 
+	html_class = GTK_HTML_CLASS (class);
+	html_class->url_requested = contact_display_url_requested;
+	html_class->link_clicked = contact_display_link_clicked;
+
 	g_object_class_install_property (
 		object_class,
 		PROP_CONTACT,
@@ -1123,12 +1142,6 @@ eab_contact_display_init (EABContactDisplay *display)
 	gtk_html_set_default_content_type (html, "text/html; charset=utf-8");
 
 	g_signal_connect (
-		display, "url-requested",
-		G_CALLBACK (contact_display_on_url_requested), display);
-	g_signal_connect (
-		display, "link-clicked",
-		G_CALLBACK (contact_display_on_link_clicked), display);
-	g_signal_connect (
 		display, "button-press-event",
 		G_CALLBACK (contact_display_button_press_event), display);
 
@@ -1170,7 +1183,7 @@ eab_contact_display_get_type (void)
 		};
 
 		type = g_type_register_static (
-			GTK_TYPE_HTML, "EABContactDisplay", &type_info, 0);
+			E_TYPE_WEB_VIEW, "EABContactDisplay", &type_info, 0);
 	}
 
 	return type;
diff --git a/addressbook/gui/widgets/eab-contact-display.h b/addressbook/gui/widgets/eab-contact-display.h
index 5a6902f..7b9707e 100644
--- a/addressbook/gui/widgets/eab-contact-display.h
+++ b/addressbook/gui/widgets/eab-contact-display.h
@@ -23,9 +23,9 @@
 #ifndef EAB_CONTACT_DISPLAY_H
 #define EAB_CONTACT_DISPLAY_H
 
-#include <gtkhtml/gtkhtml.h>
 #include <libebook/e-contact.h>
 #include <libebook/e-destination.h>
+#include <misc/e-web-view.h>
 
 /* Standard GObject macros */
 #define EAB_TYPE_CONTACT_DISPLAY \
@@ -58,12 +58,12 @@ typedef enum {
 } EABContactDisplayMode;
 
 struct _EABContactDisplay {
-	GtkHTML parent;
+	EWebView parent;
 	EABContactDisplayPrivate *priv;
 };
 
 struct _EABContactDisplayClass {
-	GtkHTMLClass parent_class;
+	EWebViewClass parent_class;
 
 	/* Signals */
 	void	(*send_message)			(EABContactDisplay *display,
diff --git a/calendar/Makefile.am b/calendar/Makefile.am
index 87057a4..ecdd2d2 100644
--- a/calendar/Makefile.am
+++ b/calendar/Makefile.am
@@ -4,7 +4,7 @@ else
 CONDUIT_DIR =
 endif
 
-SUBDIRS = idl common importers gui $(CONDUIT_DIR)
+SUBDIRS = common importers gui $(CONDUIT_DIR)
 
 error_DATA = calendar.error
 errordir = $(privdatadir)/errors
diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am
index ede213e..f05a038 100644
--- a/calendar/gui/Makefile.am
+++ b/calendar/gui/Makefile.am
@@ -2,7 +2,9 @@ if OS_WIN32
 WIN32_BOOTSTRAP_LIBS = $(top_builddir)/win32/libevolution-mail.la
 endif
 
-SUBDIRS = alarm-notify dialogs
+# KILL-BONOBO: Temporarily disabled alarm-notify
+#SUBDIRS = alarm-notify dialogs
+SUBDIRS = dialogs
 
 privsolib_LTLIBRARIES = libevolution-calendar.la
 
diff --git a/calendar/gui/alarm-notify/Makefile.am b/calendar/gui/alarm-notify/Makefile.am
index ea1c36e..d8a6838 100644
--- a/calendar/gui/alarm-notify/Makefile.am
+++ b/calendar/gui/alarm-notify/Makefile.am
@@ -1,19 +1,3 @@
-CORBA_GENERATED_H =				\
-	evolution-calendar.h
-CORBA_GENERATED_C =				\
-	evolution-calendar-common.c		\
-	evolution-calendar-skels.c		\
-	evolution-calendar-stubs.c
-CORBA_GENERATED = $(CORBA_GENERATED_C) $(CORBA_GENERATED_H)
-
-idls = $(top_srcdir)/calendar/idl/evolution-calendar.idl
-idl_flags = $(IDL_INCLUDES) 
-
-$(CORBA_GENERATED_H): $(idls)
-	$(ORBIT_IDL) $(idl_flags) $(top_srcdir)/calendar/idl/evolution-calendar.idl
-$(CORBA_GENERATED_C): $(CORBA_GENERATED_H)
-
-
 privlibexec_PROGRAMS = evolution-alarm-notify
 
 
@@ -59,19 +43,6 @@ if OS_WIN32
 evolution_alarm_notify_LDFLAGS = -mwindows
 endif
 
-server_in_files = GNOME_Evolution_Calendar_AlarmNotify.server.in.in
-server_DATA = $(server_in_files:.server.in.in=.server)
- EVO_SERVER_RULE@
- INTLTOOL_SERVER_RULE@
-
-EXTRA_DIST =			\
-	$(server_in_files)	\
-	$(glade_DATA)
-
-BUILT_SOURCES = $(CORBA_GENERATED) $(server_DATA)
-CLEANFILES = $(BUILT_SOURCES)
-
-dist-hook:
-	cd $(distdir); rm -f $(BUILT_SOURCES)
+EXTRA_DIST = $(glade_DATA)
 
 -include $(top_srcdir)/git.mk
diff --git a/calendar/gui/e-cal-component-preview.c b/calendar/gui/e-cal-component-preview.c
index 633eb56..b0ba329 100644
--- a/calendar/gui/e-cal-component-preview.c
+++ b/calendar/gui/e-cal-component-preview.c
@@ -271,83 +271,10 @@ cal_component_preview_write_html (GtkHTMLStream *stream,
 }
 
 static void
-cal_component_preview_url_requested (GtkHTML *html,
-                                     const gchar *url,
-                                     GtkHTMLStream *html_stream)
-{
-	GFile *file;
-	GFileInputStream *input_stream;
-	gchar buffer[4096];
-	gssize bytes_read;
-	GError *error = NULL;
-
-	file = g_file_new_for_uri (url);
-
-	/* XXX We only handle native files, which I guess minimizes
-	 *     the damage from doing blocking reads here.  Annoying
-	 *     that GtkHTML does not handle this itself. */
-	if (!g_file_is_native (file))
-		goto exit;
-
-	input_stream = g_file_read (file, NULL, &error);
-
-	if (error != NULL)
-		goto fail;
-
-	do {
-		bytes_read = g_input_stream_read (
-			G_INPUT_STREAM (input_stream),
-			buffer, sizeof (buffer), NULL, &error);
-
-		if (bytes_read > 0)
-			gtk_html_stream_write (
-				html_stream, buffer, bytes_read);
-
-	} while (bytes_read > 0);
-
-	if (error != NULL)
-		goto fail;
-
-	gtk_html_stream_close (html_stream, GTK_HTML_STREAM_OK);
-
-	goto exit;
-
-fail:
-	g_warning ("%s", error->message);
-	g_error_free (error);
-
-	gtk_html_stream_close (html_stream, GTK_HTML_STREAM_ERROR);
-
-exit:
-	if (input_stream != NULL)
-		g_object_unref (input_stream);
-
-	g_object_unref (file);
-}
-
-static void
-cal_component_preview_link_clicked (GtkHTML *html,
-                                    const gchar *uri)
-{
-	gpointer parent;
-
-	parent = gtk_widget_get_toplevel (GTK_WIDGET (html));
-	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
-
-	e_show_uri (parent, uri);
-}
-
-static void
 cal_component_preview_class_init (ECalComponentPreviewClass *class)
 {
-	GtkHTMLClass *gtkhtml_class;
-
 	parent_class = g_type_class_peek_parent (class);
 	g_type_class_add_private (class, sizeof (ECalComponentPreviewPrivate));
-
-	gtkhtml_class = GTK_HTML_CLASS (class);
-	gtkhtml_class->url_requested = cal_component_preview_url_requested;
-	gtkhtml_class->link_clicked = cal_component_preview_link_clicked;
 }
 
 static void
@@ -384,7 +311,8 @@ e_cal_component_preview_get_type (void)
 		};
 
 		type = g_type_register_static (
-			GTK_TYPE_HTML, "ECalComponentPreview", &type_info, 0);
+			E_TYPE_WEB_VIEW, "ECalComponentPreview",
+			&type_info, 0);
 	}
 
 	return type;
@@ -429,11 +357,3 @@ e_cal_component_preview_display (ECalComponentPreview *preview,
 		stream, ecal, comp, preview->priv->zone);
 	gtk_html_stream_close (stream, GTK_HTML_STREAM_OK);
 }
-
-void
-e_cal_component_preview_clear (ECalComponentPreview *preview)
-{
-	g_return_if_fail (E_IS_CAL_COMPONENT_PREVIEW (preview));
-
-	gtk_html_load_empty (GTK_HTML (preview));
-}
diff --git a/calendar/gui/e-cal-component-preview.h b/calendar/gui/e-cal-component-preview.h
index 8b2b0ac..78567f0 100644
--- a/calendar/gui/e-cal-component-preview.h
+++ b/calendar/gui/e-cal-component-preview.h
@@ -26,7 +26,7 @@
 
 #include <gtk/gtk.h>
 #include <libecal/e-cal.h>
-#include <gtkhtml/gtkhtml.h>
+#include <misc/e-web-view.h>
 
 /* Standard GObject macros */
 #define E_TYPE_CAL_COMPONENT_PREVIEW \
@@ -54,12 +54,12 @@ typedef struct _ECalComponentPreviewClass ECalComponentPreviewClass;
 typedef struct _ECalComponentPreviewPrivate ECalComponentPreviewPrivate;
 
 struct _ECalComponentPreview {
-	GtkHTML parent;
+	EWebView parent;
 	ECalComponentPreviewPrivate *priv;
 };
 
 struct _ECalComponentPreviewClass {
-	GtkHTMLClass parent_class;
+	EWebViewClass parent_class;
 
 	/* Notification signals */
 	void (* selection_changed) (ECalComponentPreview *preview, gint n_selected);
@@ -75,7 +75,6 @@ void		e_cal_component_preview_set_default_timezone
 void		e_cal_component_preview_display	(ECalComponentPreview *preview,
 						 ECal *ecal,
 						 ECalComponent *comp);
-void		e_cal_component_preview_clear	(ECalComponentPreview *preview);
 
 G_END_DECLS
 
diff --git a/configure.ac b/configure.ac
index 1f709dc..1ac2923 100644
--- a/configure.ac
+++ b/configure.ac
@@ -52,7 +52,6 @@ m4_define([gtk_minimum_version], [2.16.0])
 m4_define([eds_minimum_version], [evo_version])
 m4_define([gnome_icon_theme_minimum_version], [2.19.91])
 m4_define([gnome_desktop_minimum_version], [2.26.0])
-m4_define([libbonobo_minimum_version], [2.20.3])
 m4_define([libgtkhtml_minimum_version], [3.27.90])
 m4_define([gconf_minimum_version], [2.0.0])		dnl XXX Just a Guess
 m4_define([libglade_minimum_version], [2.0.0])		dnl XXX Just a Guess
@@ -238,7 +237,6 @@ PKG_CHECK_MODULES([GNOME_PLATFORM],
 	[glib-2.0 >= glib_minimum_version
 	 gtk+-2.0 >= gtk_minimum_version
 	 gconf-2.0 >= gconf_minimum_version
-	 libbonobo-2.0 >= libbonobo_minimum_version
 	 libglade-2.0 >= libglade_minimum_version
 	 libgnomecanvas-2.0 >= libgnomecanvas_minimum_version
 	 libgnomeui-2.0 >= libgnomeui_minimum_version
@@ -1383,11 +1381,6 @@ dnl Orbit / Bonobo
 dnl *************************
 AM_PATH_ORBIT2(2.9.8)
 
-AC_MSG_CHECKING([for CORBA include paths])
-IDL_INCLUDES="-I "`${PKG_CONFIG} --variable=idldir libbonobo-2.0`" -I "`${PKG_CONFIG} --variable=idldir bonobo-activation-2.0`" -I "`${PKG_CONFIG} --variable=idldir evolution-data-server-1.2`
-AC_MSG_RESULT([$IDL_INCLUDES])
-AC_SUBST(IDL_INCLUDES)
-
 dnl *****************************************************************
 dnl Utility macro to set compiler flags for a specific lib.
 dnl EVO_SET_COMPILE_FLAGS(VAR-PREFIX, DEPS, EXTRA-CFLAGS, EXTRA-LIBS)
@@ -1611,7 +1604,7 @@ fi
 dnl **********
 dnl Mail Flags
 dnl **********
-EVO_SET_COMPILE_FLAGS(EVOLUTION_MAIL, camel-provider-$EDS_PACKAGE libgnomeui-2.0 libglade-2.0 gio-2.0 gconf-2.0 gobject-2.0 libgtkhtml-$GTKHTML_PACKAGE gtkhtml-editor bonobo-activation-2.0 $mozilla_nss libebook-$EDS_PACKAGE libedataserverui-$EDS_PACKAGE unique-1.0)
+EVO_SET_COMPILE_FLAGS(EVOLUTION_MAIL, camel-provider-$EDS_PACKAGE libgnomeui-2.0 libglade-2.0 gio-2.0 gconf-2.0 gobject-2.0 libgtkhtml-$GTKHTML_PACKAGE gtkhtml-editor $mozilla_nss libebook-$EDS_PACKAGE libedataserverui-$EDS_PACKAGE unique-1.0)
 AC_SUBST(EVOLUTION_MAIL_CFLAGS)
 AC_SUBST(EVOLUTION_MAIL_LIBS)
 
@@ -1672,12 +1665,6 @@ AC_SUBST(componentdir)
 moduledir="$privlibdir/modules"
 AC_SUBST(moduledir)
 
-idldir='${datadir}'/idl/evolution-$BASE_VERSION
-AC_SUBST(idldir)
-
-serverdir="$libdir/bonobo/servers"
-AC_SUBST(serverdir)
-
 evolutionuidir="$privdatadir/ui"
 AC_SUBST(evolutionuidir)
 
@@ -1999,7 +1986,6 @@ widgets/table/Makefile
 calendar/Makefile
 calendar/importers/Makefile
 calendar/common/Makefile
-calendar/idl/Makefile
 calendar/conduits/Makefile
 calendar/conduits/common/Makefile
 calendar/conduits/todo/Makefile
diff --git a/e-util/e-marshal.list b/e-util/e-marshal.list
index d6a3f0c..c4426d4 100644
--- a/e-util/e-marshal.list
+++ b/e-util/e-marshal.list
@@ -1,4 +1,4 @@
-BOOLEAN:BOXED,POINTER,POINTER
+BOOLEAN:BOXED,STRING
 BOOLEAN:INT,INT,OBJECT,INT,INT,UINT
 BOOLEAN:INT,POINTER,INT,OBJECT,INT,INT,UINT
 BOOLEAN:NONE
diff --git a/e-util/e-plugin.c b/e-util/e-plugin.c
index 5d77dec..49a15da 100644
--- a/e-util/e-plugin.c
+++ b/e-util/e-plugin.c
@@ -31,6 +31,7 @@
 
 #include "e-plugin.h"
 #include "e-util-private.h"
+#include "e-util.h"
 
 /* plugin debug */
 #define pd(x)
@@ -487,79 +488,50 @@ e_plugin_add_load_path(const gchar *path)
 }
 
 static void
-plugin_load_subclasses (void)
+plugin_load_subclass (GType type,
+                      GHashTable *hash_table)
 {
-	GType *children;
-	guint n_children, ii;
-
-	ep_types = g_hash_table_new (g_str_hash, g_str_equal);
-	eph_types = g_hash_table_new (g_str_hash, g_str_equal);
-	ep_plugins = g_hash_table_new (g_str_hash, g_str_equal);
-
-	/* Load EPlugin subclasses. */
-
-	children = g_type_children (E_TYPE_PLUGIN, &n_children);
-
-	for (ii = 0; ii < n_children; ii++) {
-		EPluginClass *class;
-
-		class = g_type_class_ref (children[ii]);
-		g_hash_table_insert (ep_types, (gpointer) class->type, class);
-	}
+	EPluginClass *class;
 
-	g_free (children);
+	class = g_type_class_ref (type);
+	g_hash_table_insert (hash_table, (gpointer) class->type, class);
 }
 
 static void
-plugin_load_hook_subclasses (GType parent_type)
+plugin_hook_load_subclass (GType type,
+                           GHashTable *hash_table)
 {
-	GType *children;
-	guint n_children, ii;
-
-	children = g_type_children (parent_type, &n_children);
-
-	for (ii = 0; ii < n_children; ii++) {
-		EPluginHookClass *hook_class;
-		EPluginHookClass *dupe_class;
-		gpointer key;
-
-		/* First load the child's children. */
-		plugin_load_hook_subclasses (children[ii]);
-
-		/* Skip abstract types. */
-		if (G_TYPE_IS_ABSTRACT (children[ii]))
-			continue;
-
-		hook_class = g_type_class_ref (children[ii]);
-
-		/* Sanity check the hook class. */
-		if (hook_class->id == NULL || *hook_class->id == '\0') {
-			g_warning (
-				"%s has no hook ID, so skipping",
-				G_OBJECT_CLASS_NAME (hook_class));
-			g_type_class_unref (hook_class);
-			continue;
-		}
-
-		/* Check for class ID collisions. */
-		dupe_class = g_hash_table_lookup (eph_types, hook_class->id);
-		if (dupe_class != NULL) {
-			g_warning (
-				"%s and %s have the same hook "
-				"ID ('%s'), so skipping %s",
-				G_OBJECT_CLASS_NAME (dupe_class),
-				G_OBJECT_CLASS_NAME (hook_class),
-				hook_class->id,
-				G_OBJECT_CLASS_NAME (hook_class));
-			g_type_class_unref (hook_class);
-			continue;
-		}
+	EPluginHookClass *hook_class;
+	EPluginHookClass *dupe_class;
+	gpointer key;
+
+	hook_class = g_type_class_ref (type);
+
+	/* Sanity check the hook class. */
+	if (hook_class->id == NULL || *hook_class->id == '\0') {
+		g_warning (
+			"%s has no hook ID, so skipping",
+			G_OBJECT_CLASS_NAME (hook_class));
+		g_type_class_unref (hook_class);
+		return;
+	}
 
-		key = (gpointer) hook_class->id;
-		g_hash_table_insert (eph_types, key, hook_class);
+	/* Check for class ID collisions. */
+	dupe_class = g_hash_table_lookup (hash_table, hook_class->id);
+	if (dupe_class != NULL) {
+		g_warning (
+			"%s and %s have the same hook "
+			"ID ('%s'), so skipping %s",
+			G_OBJECT_CLASS_NAME (dupe_class),
+			G_OBJECT_CLASS_NAME (hook_class),
+			hook_class->id,
+			G_OBJECT_CLASS_NAME (hook_class));
+		g_type_class_unref (hook_class);
+		return;
 	}
 
-	g_free (children);
+	key = (gpointer) hook_class->id;
+	g_hash_table_insert (hash_table, key, hook_class);
 }
 
 /**
@@ -580,11 +552,19 @@ e_plugin_load_plugins(void)
 	if (eph_types != NULL)
 		return 0;
 
+	ep_types = g_hash_table_new (g_str_hash, g_str_equal);
+	eph_types = g_hash_table_new (g_str_hash, g_str_equal);
+	ep_plugins = g_hash_table_new (g_str_hash, g_str_equal);
+
 	/* We require that all GTypes for EPlugin and EPluginHook
 	 * subclasses be registered prior to loading any plugins.
 	 * It greatly simplifies the loading process. */
-	plugin_load_subclasses ();
-	plugin_load_hook_subclasses (E_TYPE_PLUGIN_HOOK);
+	e_type_traverse (
+		E_TYPE_PLUGIN, (ETypeFunc)
+		plugin_load_subclass, ep_types);
+	e_type_traverse (
+		E_TYPE_PLUGIN_HOOK, (ETypeFunc)
+		plugin_hook_load_subclass, eph_types);
 
 	client = gconf_client_get_default ();
 	ep_disabled = gconf_client_get_list (
diff --git a/e-util/e-util.c b/e-util/e-util.c
index 57ec251..3c004e5 100644
--- a/e-util/e-util.c
+++ b/e-util/e-util.c
@@ -407,6 +407,47 @@ e_radio_action_get_current_action (GtkRadioAction *radio_action)
 }
 
 /**
+ * e_type_traverse:
+ * @parent_type: the root #GType to traverse from
+ * @func: the function to call for each visited #GType
+ * @user_data: user data to pass to the function
+ *
+ * Calls @func for all instantiable subtypes of @parent_type.
+ *
+ * This is often useful for extending functionality by way of #EModule.
+ * A module may register a subtype of @parent_type in its e_module_load()
+ * function.  Then later on the application will call e_type_traverse()
+ * to instantiate all registered subtypes of @parent_type.
+ **/
+void
+e_type_traverse (GType parent_type,
+                 ETypeFunc func,
+                 gpointer user_data)
+{
+	GType *children;
+	guint n_children, ii;
+
+	g_return_if_fail (func != NULL);
+
+	children = g_type_children (parent_type, &n_children);
+
+	for (ii = 0; ii < n_children; ii++) {
+		GType type = children[ii];
+
+		/* Recurse over the child's children. */
+		e_type_traverse (type, func, user_data);
+
+		/* Skip abstract types. */
+		if (G_TYPE_IS_ABSTRACT (type))
+			continue;
+
+		func (type, user_data);
+	}
+
+	g_free (children);
+}
+
+/**
  * e_str_without_underscores:
  * @s: the string to strip underscores from.
  *
diff --git a/e-util/e-util.h b/e-util/e-util.h
index 33bc940..1e23209 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -41,6 +41,8 @@ typedef enum {
 	E_FOCUS_END
 } EFocus;
 
+typedef void (*ETypeFunc) (GType type, gpointer user_data);
+
 const gchar *	e_get_user_data_dir		(void);
 const gchar *	e_get_accels_filename		(void);
 void		e_show_uri			(GtkWindow *parent,
@@ -59,6 +61,9 @@ void		e_action_group_remove_all_actions
 						(GtkActionGroup *action_group);
 GtkRadioAction *e_radio_action_get_current_action
 						(GtkRadioAction *radio_action);
+void		e_type_traverse			(GType parent_type,
+						 ETypeFunc func,
+						 gpointer user_data);
 
 gchar *		e_str_without_underscores	(const gchar *s);
 gint		e_str_compare			(gconstpointer x,
diff --git a/evolution-shell.pc.in b/evolution-shell.pc.in
index b94dd85..c9e4ade 100644
--- a/evolution-shell.pc.in
+++ b/evolution-shell.pc.in
@@ -17,8 +17,6 @@ imagesdir= imagesdir@
 
 execversion= BASE_VERSION@
 
-IDL_INCLUDES=-I ${idldir} @IDL_INCLUDES@
-
 Name: evolution-shell
 Description: libraries needed for Evolution shell components
 Version: @VERSION@
diff --git a/mail/e-mail-browser.c b/mail/e-mail-browser.c
index ea860c0..69199bf 100644
--- a/mail/e-mail-browser.c
+++ b/mail/e-mail-browser.c
@@ -123,22 +123,20 @@ static GtkActionEntry mail_browser_entries[] = {
 
 static void
 mail_browser_menu_item_select_cb (EMailBrowser *browser,
-                                  GtkWidget *menu_item)
+                                  GtkWidget *widget)
 {
 	GtkAction *action;
 	GtkStatusbar *statusbar;
-	gchar *tooltip = NULL;
+	const gchar *tooltip;
 	guint context_id;
 	gpointer data;
 
-	action = g_object_get_data (G_OBJECT (menu_item), "action");
-	g_return_if_fail (GTK_IS_ACTION (action));
+	action = gtk_widget_get_action (widget);
+	tooltip = gtk_action_get_tooltip (action);
 
-	data = g_object_get_data (G_OBJECT (menu_item), "context-id");
+	data = g_object_get_data (G_OBJECT (widget), "context-id");
 	context_id = GPOINTER_TO_UINT (data);
 
-	g_object_get (action, "tooltip", &tooltip, NULL);
-
 	if (tooltip == NULL)
 		return;
 
@@ -175,11 +173,6 @@ mail_browser_connect_proxy_cb (EMailBrowser *browser,
 	statusbar = GTK_STATUSBAR (browser->priv->statusbar);
 	context_id = gtk_statusbar_get_context_id (statusbar, G_STRFUNC);
 
-	g_object_set_data_full (
-		G_OBJECT (proxy),
-		"action", g_object_ref (action),
-		(GDestroyNotify) g_object_unref);
-
 	g_object_set_data (
 		G_OBJECT (proxy), "context-id",
 		GUINT_TO_POINTER (context_id));
@@ -440,7 +433,7 @@ mail_browser_constructed (GObject *object)
 	priv->statusbar = g_object_ref (widget);
 	gtk_widget_show (widget);
 
-	widget = e_mail_search_bar_new (EM_FORMAT_HTML (html_display)->html);
+	widget = e_mail_search_bar_new (E_WEB_VIEW (html));
 	gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
 	priv->search_bar = g_object_ref (widget);
 	gtk_widget_hide (widget);
diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c
index c29328b..df65c25 100644
--- a/mail/e-mail-display.c
+++ b/mail/e-mail-display.c
@@ -22,7 +22,6 @@
 #include "e-mail-display.h"
 
 #include <config.h>
-#include <string.h>
 #include <glib/gi18n.h>
 
 #include "e-util/e-util.h"
@@ -36,162 +35,30 @@
 
 struct _EMailDisplayPrivate {
 	EMFormatHTML *formatter;
-	GtkUIManager *ui_manager;
-	gchar *selected_uri;
 };
 
 enum {
 	PROP_0,
-	PROP_ANIMATE,
-	PROP_CARET_MODE,
-	PROP_FORMATTER,
-	PROP_SELECTED_URI
-};
-
-enum {
-	POPUP_EVENT,
-	STATUS_MESSAGE,
-	LAST_SIGNAL
+	PROP_FORMATTER
 };
 
 static gpointer parent_class;
-static guint signals[LAST_SIGNAL];
 
 static const gchar *ui =
 "<ui>"
 "  <popup name='context'>"
-"    <menuitem action='http-open'/>"
-"    <menuitem action='send-message'/>"
-"    <menuitem action='uri-copy'/>"
-"    <menuitem action='add-to-address-book'/>"
-"    <menuitem action='mailto-copy'/>"
-"    <menu action='search-folder-menu'>"
-"      <menuitem action='search-folder-sender'/>"
-"      <menuitem action='search-folder-recipient'/>"
-"    </menu>"
+"    <placeholder name='custom-actions-1'>"
+"      <menuitem action='add-to-address-book'/>"
+"    </placeholder>"
+"    <placeholder name='custom-actions-3'>"
+"      <menu action='search-folder-menu'>"
+"        <menuitem action='search-folder-recipient'/>"
+"        <menuitem action='search-folder-sender'/>"
+"      </menu>"
+"    </placeholder>"
 "  </popup>"
 "</ui>";
 
-static void
-action_add_to_address_book_cb (GtkAction *action,
-                               EMailDisplay *display)
-{
-	CamelURL *curl;
-	const gchar *uri;
-	gpointer parent;
-
-	parent = gtk_widget_get_toplevel (GTK_WIDGET (display));
-	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
-
-	uri = e_mail_display_get_selected_uri (display);
-	g_return_if_fail (uri != NULL);
-
-	/* This should work because we checked it in update_actions(). */
-	curl = camel_url_new (uri, NULL);
-	g_return_if_fail (curl != NULL);
-
-	if (curl->path != NULL && *curl->path != '\0')
-		em_utils_add_address (parent, curl->path);
-
-	camel_url_free (curl);
-}
-
-static void
-action_http_open_cb (GtkAction *action,
-                     EMailDisplay *display)
-{
-	const gchar *uri;
-	gpointer parent;
-
-	parent = gtk_widget_get_toplevel (GTK_WIDGET (display));
-	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
-
-	uri = e_mail_display_get_selected_uri (display);
-	g_return_if_fail (uri != NULL);
-
-	e_show_uri (parent, uri);
-}
-
-static void
-action_mailto_copy_cb (GtkAction *action,
-                       EMailDisplay *display)
-{
-	CamelURL *curl;
-	CamelInternetAddress *inet_addr;
-	GtkClipboard *clipboard;
-	const gchar *uri;
-	gchar *text;
-
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-	uri = e_mail_display_get_selected_uri (display);
-	g_return_if_fail (uri != NULL);
-
-	/* This should work because we checked it in update_actions(). */
-	curl = camel_url_new (uri, NULL);
-	g_return_if_fail (curl != NULL);
-
-	inet_addr = camel_internet_address_new ();
-	camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
-	text = camel_address_encode (CAMEL_ADDRESS (inet_addr));
-	if (text == NULL || *text == '\0')
-		text = g_strdup (uri + strlen ("mailto:";));
-
-	camel_object_unref (inet_addr);
-	camel_url_free (curl);
-
-	gtk_clipboard_set_text (clipboard, text, -1);
-	gtk_clipboard_store (clipboard);
-
-	g_free (text);
-}
-
-static void
-action_send_message_cb (GtkAction *action,
-                        EMailDisplay *display)
-{
-	const gchar *uri;
-
-	uri = e_mail_display_get_selected_uri (display);
-	g_return_if_fail (uri != NULL);
-
-	em_utils_compose_new_message_with_mailto (uri, NULL);
-}
-
-static void
-action_uri_copy_cb (GtkAction *action,
-                    EMailDisplay *display)
-{
-	GtkClipboard *clipboard;
-	const gchar *uri;
-
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-	uri = e_mail_display_get_selected_uri (display);
-	g_return_if_fail (uri != NULL);
-
-	gtk_clipboard_set_text (clipboard, uri, -1);
-	gtk_clipboard_store (clipboard);
-}
-
-static GtkActionEntry uri_entries[] = {
-
-	{ "uri-copy",
-	  GTK_STOCK_COPY,
-	  N_("_Copy Link Location"),
-	  NULL,
-	  NULL,  /* XXX Add a tooltip! */
-	  G_CALLBACK (action_uri_copy_cb) },
-};
-
-static GtkActionEntry http_entries[] = {
-
-	{ "http-open",
-	  "emblem-web",
-	  N_("_Open Link in Browser"),
-	  NULL,
-	  NULL,  /* XXX Add a tooltip! */
-	  G_CALLBACK (action_http_open_cb) },
-};
-
 static GtkActionEntry mailto_entries[] = {
 
 	{ "add-to-address-book",
@@ -199,14 +66,7 @@ static GtkActionEntry mailto_entries[] = {
 	  N_("_Add to Address Book..."),
 	  NULL,
 	  NULL,  /* XXX Add a tooltip! */
-	  G_CALLBACK (action_add_to_address_book_cb) },
-
-	{ "mailto-copy",
-	  GTK_STOCK_COPY,
-	  N_("_Copy Email Address"),
-	  NULL,
-	  NULL,  /* XXX Add a tooltip! */
-	  G_CALLBACK (action_mailto_copy_cb) },
+	  NULL   /* Handled by EMailReader */ },
 
 	{ "search-folder-recipient",
 	  NULL,
@@ -222,13 +82,6 @@ static GtkActionEntry mailto_entries[] = {
 	  NULL,  /* XXX Add a tooltip! */
 	  NULL   /* Handled by EMailReader */ },
 
-	{ "send-message",
-	  "mail-message-new",
-	  N_("_Send New Message To..."),
-	  NULL,
-	  NULL,  /* XXX Add a tooltip! */
-	  G_CALLBACK (action_send_message_cb) },
-
 	/*** Menus ***/
 
 	{ "search-folder-menu",
@@ -239,123 +92,6 @@ static GtkActionEntry mailto_entries[] = {
 	  NULL }
 };
 
-static gboolean
-mail_display_emit_popup_event (EMailDisplay *display,
-                               GdkEventButton *event,
-                               const gchar *uri,
-                               EMFormatPURI *puri)
-{
-	CamelMimePart *mime_part;
-	gboolean stop_handlers = FALSE;
-
-	mime_part = (puri != NULL) ? puri->part : NULL;
-
-	g_signal_emit (
-		display, signals[POPUP_EVENT], 0,
-		event, uri, mime_part, &stop_handlers);
-
-	return stop_handlers;
-}
-
-static void
-mail_display_emit_status_message (EMailDisplay *display,
-                                  const gchar *status_message)
-{
-	g_signal_emit (display, signals[STATUS_MESSAGE], 0, status_message);
-}
-
-static void
-mail_display_get_uri_puri (EMailDisplay *display,
-                           GdkEventButton *event,
-                           GtkHTML *html,
-                           gchar **uri,
-                           EMFormatPURI **puri)
-{
-	EMFormat *formatter;
-	gchar *text_uri;
-	gchar *image_uri;
-	gboolean is_cid;
-
-	formatter = EM_FORMAT (display->priv->formatter);
-
-	if (event != NULL) {
-		text_uri = gtk_html_get_url_at (html, event->x, event->y);
-		image_uri = gtk_html_get_image_src_at (html, event->x, event->y);
-	} else {
-		text_uri = gtk_html_get_cursor_url (html);
-		image_uri = gtk_html_get_cursor_image_src (html);
-	}
-
-	is_cid = (image_uri != NULL) &&
-		(g_ascii_strncasecmp (image_uri, "cid:", 4) == 0);
-
-	if (image_uri != NULL) {
-		if (strstr (image_uri, "://") == NULL && !is_cid) {
-			gchar *temp;
-
-			temp = g_strconcat ("file://", image_uri, NULL);
-			g_free (image_uri);
-			image_uri = temp;
-		}
-	}
-
-	if (puri != NULL) {
-		if (text_uri != NULL)
-			*puri = em_format_find_puri (formatter, text_uri);
-
-		if (*puri == NULL && image_uri != NULL)
-			*puri = em_format_find_puri (formatter, image_uri);
-	}
-
-	if (uri != NULL) {
-		*uri = NULL;
-		if (is_cid) {
-			if (text_uri != NULL)
-				*uri = g_strdup_printf (
-					"%s\n%s", text_uri, image_uri);
-			else {
-				*uri = image_uri;
-				image_uri = NULL;
-			}
-		} else {
-			*uri = text_uri;
-			text_uri = NULL;
-		}
-	}
-
-	g_free (text_uri);
-	g_free (image_uri);
-}
-
-static gboolean
-mail_display_button_press_event_cb (EMailDisplay *display,
-                                    GdkEventButton *event,
-                                    GtkHTML *html)
-{
-	EMFormatPURI *puri = NULL;
-	gboolean finished = TRUE;
-	gchar *uri = NULL;
-
-	/* The GtkHTML object may be the EMailDisplay itself
-	 * or an inner iframe. */
-
-	if (event->button != 3)
-		return FALSE;
-
-	mail_display_get_uri_puri (display, event, html, &uri, &puri);
-
-	if (uri == NULL || g_str_has_prefix (uri, "##")) {
-		g_free (uri);
-		return FALSE;
-	}
-
-	finished = mail_display_emit_popup_event (display, event, uri, puri);
-
-	g_free (uri);
-
-	return finished;
-}
-
 static void
 mail_display_update_formatter_colors (EMailDisplay *display)
 {
@@ -404,29 +140,11 @@ mail_display_set_property (GObject *object,
                            GParamSpec *pspec)
 {
 	switch (property_id) {
-		case PROP_ANIMATE:
-			e_mail_display_set_animate (
-				E_MAIL_DISPLAY (object),
-				g_value_get_boolean (value));
-			return;
-
-		case PROP_CARET_MODE:
-			e_mail_display_set_caret_mode (
-				E_MAIL_DISPLAY (object),
-				g_value_get_boolean (value));
-			return;
-
 		case PROP_FORMATTER:
 			e_mail_display_set_formatter (
 				E_MAIL_DISPLAY (object),
 				g_value_get_object (value));
 			return;
-
-		case PROP_SELECTED_URI:
-			e_mail_display_set_selected_uri (
-				E_MAIL_DISPLAY (object),
-				g_value_get_string (value));
-			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -439,29 +157,11 @@ mail_display_get_property (GObject *object,
                            GParamSpec *pspec)
 {
 	switch (property_id) {
-		case PROP_ANIMATE:
-			g_value_set_boolean (
-				value, e_mail_display_get_animate (
-				E_MAIL_DISPLAY (object)));
-			return;
-
-		case PROP_CARET_MODE:
-			g_value_set_boolean (
-				value, e_mail_display_get_caret_mode (
-				E_MAIL_DISPLAY (object)));
-			return;
-
 		case PROP_FORMATTER:
 			g_value_set_object (
 				value, e_mail_display_get_formatter (
 				E_MAIL_DISPLAY (object)));
 			return;
-
-		case PROP_SELECTED_URI:
-			g_value_set_string (
-				value, e_mail_display_get_selected_uri (
-				E_MAIL_DISPLAY (object)));
-			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -479,11 +179,6 @@ mail_display_dispose (GObject *object)
 		priv->formatter = NULL;
 	}
 
-	if (priv->ui_manager) {
-		g_object_unref (priv->ui_manager);
-		priv->ui_manager = NULL;
-	}
-
 	/* Chain up to parent's dispose() method. */
 	G_OBJECT_CLASS (parent_class)->dispose (object);
 }
@@ -512,39 +207,13 @@ mail_display_style_set (GtkWidget *widget,
 	em_format_redraw (EM_FORMAT (priv->formatter));
 }
 
-static gboolean
-mail_display_button_press_event (GtkWidget *widget,
-                                 GdkEventButton *event)
-{
-	EMailDisplay *display = E_MAIL_DISPLAY (widget);
-	GtkHTML *html = GTK_HTML (widget);
-
-	if (mail_display_button_press_event_cb (display, event, html))
-		return TRUE;
-
-	/* Chain up to parent's button_press_event() method. */
-	return GTK_WIDGET_CLASS (parent_class)->
-		button_press_event (widget, event);
-}
-
-static gboolean
-mail_display_scroll_event (GtkWidget *widget,
-                           GdkEventScroll *event)
+static void
+mail_display_url_requested (GtkHTML *html,
+                            const gchar *uri,
+                            GtkHTMLStream *stream)
 {
-	if (event->state & GDK_CONTROL_MASK) {
-		switch (event->direction) {
-			case GDK_SCROLL_UP:
-				gtk_html_zoom_in (GTK_HTML (widget));
-				return TRUE;
-			case GDK_SCROLL_DOWN:
-				gtk_html_zoom_out (GTK_HTML (widget));
-				return TRUE;
-			default:
-				break;
-		}
-	}
-
-	return FALSE;
+	/* XXX Sadly, we must block the default method
+	 *     until EMFormatHTML is made asynchronous. */
 }
 
 static void
@@ -591,89 +260,12 @@ mail_display_link_clicked (GtkHTML *html,
 		/* ignore */ ;
 
 	else {
-		gpointer parent;
-
-		parent = gtk_widget_get_toplevel (GTK_WIDGET (html));
-		parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
-
-		e_show_uri (parent, uri);
+		/* Chain up to parent's link_clicked() method. */
+		GTK_HTML_CLASS (parent_class)->link_clicked (html, uri);
 	}
 }
 
 static void
-mail_display_on_url (GtkHTML *html,
-                     const gchar *uri)
-{
-	EMailDisplay *display;
-	CamelInternetAddress *address;
-	CamelURL *curl;
-	const gchar *format = NULL;
-	gchar *message = NULL;
-	gchar *who;
-
-	display = E_MAIL_DISPLAY (html);
-
-	if (uri == NULL || *uri == '\0')
-		goto exit;
-
-	if (g_str_has_prefix (uri, "mailto:";))
-		format = _("Click to mail %s");
-	else if (g_str_has_prefix (uri, "callto:"))
-		format = _("Click to call %s");
-	else if (g_str_has_prefix (uri, "h323:"))
-		format = _("Click to call %s");
-	else if (g_str_has_prefix (uri, "sip:"))
-		format = _("Click to call %s");
-	else if (g_str_has_prefix (uri, "##"))
-		message = g_strdup (_("Click to hide/unhide addresses"));
-	else
-		message = g_strdup_printf (_("Click to open %s"), uri);
-
-	if (format == NULL)
-		goto exit;
-
-	curl = camel_url_new (uri, NULL);
-	address = camel_internet_address_new ();
-	camel_address_decode (CAMEL_ADDRESS (address), curl->path);
-	who = camel_address_format (CAMEL_ADDRESS (address));
-	camel_object_unref (address);
-	camel_url_free (curl);
-
-	if (who == NULL)
-		who = g_strdup (strchr (uri, ':') + 1);
-
-	message = g_strdup_printf (format, who);
-
-	g_free (who);
-
-exit:
-	mail_display_emit_status_message (display, message);
-
-	g_free (message);
-}
-
-static void
-mail_display_iframe_created (GtkHTML *html,
-                             GtkHTML *iframe)
-{
-	g_signal_connect_swapped (
-		iframe, "button-press-event",
-		G_CALLBACK (mail_display_button_press_event_cb), html);
-}
-
-static gboolean
-mail_display_popup_event (EMailDisplay *display,
-                          GdkEventButton *event,
-                          const gchar *uri,
-                          EMFormatPURI *puri)
-{
-	e_mail_display_set_selected_uri (display, uri);
-	e_mail_display_show_popup_menu (display, event, NULL, NULL);
-
-	return TRUE;
-}
-
-static void
 mail_display_class_init (EMailDisplayClass *class)
 {
 	GObjectClass *object_class;
@@ -691,35 +283,10 @@ mail_display_class_init (EMailDisplayClass *class)
 	widget_class = GTK_WIDGET_CLASS (class);
 	widget_class->realize = mail_display_realize;
 	widget_class->style_set = mail_display_style_set;
-	widget_class->button_press_event = mail_display_button_press_event;
-	widget_class->scroll_event = mail_display_scroll_event;
 
 	html_class = GTK_HTML_CLASS (class);
+	html_class->url_requested = mail_display_url_requested;
 	html_class->link_clicked = mail_display_link_clicked;
-	html_class->on_url = mail_display_on_url;
-	html_class->iframe_created = mail_display_iframe_created;
-
-	class->popup_event = mail_display_popup_event;
-
-	g_object_class_install_property (
-		object_class,
-		PROP_ANIMATE,
-		g_param_spec_boolean (
-			"animate",
-			"Animate Images",
-			NULL,
-			FALSE,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property (
-		object_class,
-		PROP_CARET_MODE,
-		g_param_spec_boolean (
-			"caret-mode",
-			"Caret Mode",
-			NULL,
-			FALSE,
-			G_PARAM_READWRITE));
 
 	g_object_class_install_property (
 		object_class,
@@ -730,67 +297,27 @@ mail_display_class_init (EMailDisplayClass *class)
 			NULL,
 			EM_TYPE_FORMAT_HTML,
 			G_PARAM_READWRITE));
-
-	g_object_class_install_property (
-		object_class,
-		PROP_SELECTED_URI,
-		g_param_spec_string (
-			"selected-uri",
-			"Selected URI",
-			NULL,
-			NULL,
-			G_PARAM_READWRITE));
-
-	signals[POPUP_EVENT] = g_signal_new (
-		"popup-event",
-		G_TYPE_FROM_CLASS (class),
-		G_SIGNAL_RUN_LAST,
-		G_STRUCT_OFFSET (EMailDisplayClass, popup_event),
-		g_signal_accumulator_true_handled, NULL,
-		e_marshal_BOOLEAN__BOXED_POINTER_POINTER,
-		G_TYPE_BOOLEAN, 3,
-		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE,
-		G_TYPE_POINTER,
-		G_TYPE_POINTER);
-
-	signals[STATUS_MESSAGE] = g_signal_new (
-		"status-message",
-		G_TYPE_FROM_CLASS (class),
-		G_SIGNAL_RUN_FIRST,
-		G_STRUCT_OFFSET (EMailDisplayClass, status_message),
-		NULL, NULL,
-		g_cclosure_marshal_VOID__STRING,
-		G_TYPE_NONE, 1,
-		G_TYPE_STRING);
 }
 
 static void
 mail_display_init (EMailDisplay *display)
 {
+	EWebView *web_view;
 	GtkUIManager *ui_manager;
 	GtkActionGroup *action_group;
-	const gchar *id;
 	GError *error = NULL;
 
-	display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display);
-
-	ui_manager = gtk_ui_manager_new ();
-	display->priv->ui_manager = ui_manager;
-
-	action_group = e_mail_display_add_action_group (display, "uri");
+	web_view = E_WEB_VIEW (display);
 
-	gtk_action_group_add_actions (
-		action_group, uri_entries,
-		G_N_ELEMENTS (uri_entries), display);
-
-	action_group = e_mail_display_add_action_group (display, "http");
-
-	gtk_action_group_add_actions (
-		action_group, http_entries,
-		G_N_ELEMENTS (http_entries), display);
+	display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display);
 
-	action_group = e_mail_display_add_action_group (display, "mailto");
+	/* EWebView's action groups are added during its instance
+	 * initialization function (like what we're in now), so it
+	 * is safe to fetch them this early in construction. */
+	action_group = e_web_view_get_action_group (web_view, "mailto");
 
+	/* We don't actually handle the actions we're adding.
+	 * EMailReader handles them.  How devious is that? */
 	gtk_action_group_add_actions (
 		action_group, mailto_entries,
 		G_N_ELEMENTS (mailto_entries), display);
@@ -798,13 +325,10 @@ mail_display_init (EMailDisplay *display)
 	/* Because we are loading from a hard-coded string, there is
 	 * no chance of I/O errors.  Failure here implies a malformed
 	 * UI definition.  Full stop. */
+	ui_manager = e_web_view_get_ui_manager (web_view);
 	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
 	if (error != NULL)
 		g_error ("%s", error->message);
-
-	id = "org.gnome.evolution.mail.display";
-	e_plugin_ui_register_manager (ui_manager, id, display);
-	e_plugin_ui_enable_manager (ui_manager, id);
 }
 
 GType
@@ -827,64 +351,12 @@ e_mail_display_get_type (void)
 		};
 
 		type = g_type_register_static (
-			GTK_TYPE_HTML, "EMailDisplay", &type_info, 0);
+			E_TYPE_WEB_VIEW, "EMailDisplay", &type_info, 0);
 	}
 
 	return type;
 }
 
-gboolean
-e_mail_display_get_animate (EMailDisplay *display)
-{
-	/* XXX This is just here to maintain symmetry
-	 *     with e_mail_display_set_animate(). */
-
-	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
-
-	return gtk_html_get_animate (GTK_HTML (display));
-}
-
-void
-e_mail_display_set_animate (EMailDisplay *display,
-                            gboolean animate)
-{
-	/* XXX GtkHTML does not utilize GObject properties as well
-	 *     as it could.  This just wraps gtk_html_set_animate()
-	 *     so we can get a "notify::animate" signal. */
-
-	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
-
-	gtk_html_set_animate (GTK_HTML (display), animate);
-
-	g_object_notify (G_OBJECT (display), "animate");
-}
-
-gboolean
-e_mail_display_get_caret_mode (EMailDisplay *display)
-{
-	/* XXX This is just here to maintain symmetry
-	 *     with e_mail_display_set_caret_mode(). */
-
-	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
-
-	return gtk_html_get_caret_mode (GTK_HTML (display));
-}
-
-void
-e_mail_display_set_caret_mode (EMailDisplay *display,
-                               gboolean caret_mode)
-{
-	/* XXX GtkHTML does not utilize GObject properties as well
-	 *     as it could.  This just wraps gtk_html_set_caret_mode()
-	 *     so we can get a "notify::caret-mode" signal. */
-
-	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
-
-	gtk_html_set_caret_mode (GTK_HTML (display), caret_mode);
-
-	g_object_notify (G_OBJECT (display), "caret-mode");
-}
-
 EMFormatHTML *
 e_mail_display_get_formatter (EMailDisplay *display)
 {
@@ -907,166 +379,3 @@ e_mail_display_set_formatter (EMailDisplay *display,
 
 	g_object_notify (G_OBJECT (display), "formatter");
 }
-
-const gchar *
-e_mail_display_get_selected_uri (EMailDisplay *display)
-{
-	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
-
-	return display->priv->selected_uri;
-}
-
-void
-e_mail_display_set_selected_uri (EMailDisplay *display,
-                                 const gchar *selected_uri)
-{
-	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
-
-	g_free (display->priv->selected_uri);
-	display->priv->selected_uri = g_strdup (selected_uri);
-
-	g_object_notify (G_OBJECT (display), "selected-uri");
-}
-
-GtkAction *
-e_mail_display_get_action (EMailDisplay *display,
-                           const gchar *action_name)
-{
-	GtkUIManager *ui_manager;
-
-	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
-	g_return_val_if_fail (action_name != NULL, NULL);
-
-	ui_manager = e_mail_display_get_ui_manager (display);
-
-	return e_lookup_action (ui_manager, action_name);
-}
-
-GtkActionGroup *
-e_mail_display_add_action_group (EMailDisplay *display,
-                                 const gchar *group_name)
-{
-	GtkActionGroup *action_group;
-	GtkUIManager *ui_manager;
-	const gchar *domain;
-
-	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
-	g_return_val_if_fail (group_name != NULL, NULL);
-
-	ui_manager = e_mail_display_get_ui_manager (display);
-	domain = GETTEXT_PACKAGE;
-
-	action_group = gtk_action_group_new (group_name);
-	gtk_action_group_set_translation_domain (action_group, domain);
-	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
-	g_object_unref (action_group);
-
-	return action_group;
-}
-
-GtkActionGroup *
-e_mail_display_get_action_group (EMailDisplay *display,
-                                 const gchar *group_name)
-{
-	GtkUIManager *ui_manager;
-
-	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
-	g_return_val_if_fail (group_name != NULL, NULL);
-
-	ui_manager = e_mail_display_get_ui_manager (display);
-
-	return e_lookup_action_group (ui_manager, group_name);
-}
-
-GtkWidget *
-e_mail_display_get_popup_menu (EMailDisplay *display)
-{
-	GtkUIManager *ui_manager;
-	GtkWidget *menu;
-
-	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
-
-	ui_manager = e_mail_display_get_ui_manager (display);
-	menu = gtk_ui_manager_get_widget (ui_manager, "/context");
-	g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
-
-	return menu;
-}
-
-GtkUIManager *
-e_mail_display_get_ui_manager (EMailDisplay *display)
-{
-	EMailDisplayPrivate *priv;
-
-	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
-
-	priv = E_MAIL_DISPLAY_GET_PRIVATE (display);
-
-	return priv->ui_manager;
-}
-
-void
-e_mail_display_show_popup_menu (EMailDisplay *display,
-                                GdkEventButton *event,
-                                GtkMenuPositionFunc func,
-                                gpointer user_data)
-{
-	GtkWidget *menu;
-
-	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
-
-	e_mail_display_update_actions (display);
-
-	menu = e_mail_display_get_popup_menu (display);
-
-	if (event != NULL)
-		gtk_menu_popup (
-			GTK_MENU (menu), NULL, NULL, func,
-			user_data, event->button, event->time);
-	else
-		gtk_menu_popup (
-			GTK_MENU (menu), NULL, NULL, func,
-			user_data, 0, gtk_get_current_event_time ());
-}
-
-void
-e_mail_display_update_actions (EMailDisplay *display)
-{
-	CamelURL *curl;
-	GtkActionGroup *action_group;
-	gboolean scheme_is_http;
-	gboolean scheme_is_mailto;
-	gboolean uri_is_valid;
-	gboolean visible;
-	const gchar *uri;
-
-	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
-
-	uri = e_mail_display_get_selected_uri (display);
-	g_return_if_fail (uri != NULL);
-
-	/* Parse the URI early so we know if the actions will work. */
-	curl = camel_url_new (uri, NULL);
-	uri_is_valid = (curl != NULL);
-	camel_url_free (curl);
-
-	scheme_is_http =
-		(g_ascii_strncasecmp (uri, "http:", 5) == 0) ||
-		(g_ascii_strncasecmp (uri, "https:", 6) == 0);
-
-	scheme_is_mailto =
-		(g_ascii_strncasecmp (uri, "mailto:";, 7) == 0);
-
-	/* Allow copying the URI even if it's malformed. */
-	visible = !scheme_is_mailto;
-	action_group = e_mail_display_get_action_group (display, "uri");
-	gtk_action_group_set_visible (action_group, visible);
-
-	visible = uri_is_valid && scheme_is_http;
-	action_group = e_mail_display_get_action_group (display, "http");
-	gtk_action_group_set_visible (action_group, visible);
-
-	visible = uri_is_valid && scheme_is_mailto;
-	action_group = e_mail_display_get_action_group (display, "mailto");
-	gtk_action_group_set_visible (action_group, visible);
-}
diff --git a/mail/e-mail-display.h b/mail/e-mail-display.h
index 9f273fb..1b71a9d 100644
--- a/mail/e-mail-display.h
+++ b/mail/e-mail-display.h
@@ -22,8 +22,8 @@
 #ifndef E_MAIL_DISPLAY_H
 #define E_MAIL_DISPLAY_H
 
-#include <gtkhtml/gtkhtml.h>
 #include <mail/em-format-html.h>
+#include <misc/e-web-view.h>
 
 /* Standard GObject macros */
 #define E_TYPE_MAIL_DISPLAY \
@@ -51,48 +51,18 @@ typedef struct _EMailDisplayClass EMailDisplayClass;
 typedef struct _EMailDisplayPrivate EMailDisplayPrivate;
 
 struct _EMailDisplay {
-	GtkHTML parent;
+	EWebView parent;
 	EMailDisplayPrivate *priv;
 };
 
 struct _EMailDisplayClass {
-	GtkHTMLClass parent_class;
-
-	/* Signals */
-	gboolean	(*popup_event)		(EMailDisplay *display,
-						 GdkEventButton *event,
-						 const gchar *uri,
-						 EMFormatPURI *puri);
-	void		(*status_message)	(EMailDisplay *display,
-						 const gchar *status_message);
+	EWebViewClass parent_class;
 };
 
 GType		e_mail_display_get_type		(void);
-gboolean	e_mail_display_get_animate	(EMailDisplay *display);
-void		e_mail_display_set_animate	(EMailDisplay *display,
-						 gboolean animate);
-gboolean	e_mail_display_get_caret_mode	(EMailDisplay *display);
-void		e_mail_display_set_caret_mode	(EMailDisplay *display,
-						 gboolean caret_mode);
 EMFormatHTML *	e_mail_display_get_formatter	(EMailDisplay *display);
 void		e_mail_display_set_formatter	(EMailDisplay *display,
 						 EMFormatHTML *formatter);
-const gchar *	e_mail_display_get_selected_uri	(EMailDisplay *display);
-void		e_mail_display_set_selected_uri	(EMailDisplay *display,
-						 const gchar *uri);
-GtkAction *	e_mail_display_get_action	(EMailDisplay *display,
-						 const gchar *action_name);
-GtkActionGroup *e_mail_display_add_action_group	(EMailDisplay *display,
-						 const gchar *group_name);
-GtkActionGroup *e_mail_display_get_action_group	(EMailDisplay *display,
-						 const gchar *group_name);
-GtkWidget *	e_mail_display_get_popup_menu	(EMailDisplay *display);
-GtkUIManager *	e_mail_display_get_ui_manager	(EMailDisplay *display);
-void		e_mail_display_show_popup_menu	(EMailDisplay *display,
-						 GdkEventButton *event,
-						 GtkMenuPositionFunc func,
-						 gpointer user_data);
-void		e_mail_display_update_actions	(EMailDisplay *display);
 
 G_END_DECLS
 
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index 72f7918..d3a845c 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -67,15 +67,16 @@ static void
 action_mail_add_sender_cb (GtkAction *action,
                            EMailReader *reader)
 {
+	EShell *shell;
+	EShellBackend *shell_backend;
 	MessageList *message_list;
 	CamelMessageInfo *info;
 	CamelFolder *folder;
-	GtkWindow *window;
 	GPtrArray *uids;
 	const gchar *address;
 
 	message_list = e_mail_reader_get_message_list (reader);
-	window = e_mail_reader_get_window (reader);
+	shell_backend = e_mail_reader_get_shell_backend (reader);
 
 	folder = message_list->folder;
 	uids = message_list_get_selected (message_list);
@@ -91,13 +92,52 @@ action_mail_add_sender_cb (GtkAction *action,
 	if (address == NULL || *address == '\0')
 		goto exit;
 
-	em_utils_add_address (window, address);
+	/* XXX EBookShellBackend should be listening for this
+	 *     event.  Kind of kludgey, but works for now. */
+	shell = e_shell_backend_get_shell (shell_backend);
+	e_shell_event (shell, "contact-quick-add-email", (gpointer) address);
 
 exit:
 	em_utils_uids_free (uids);
 }
 
 static void
+action_add_to_address_book_cb (GtkAction *action,
+                               EMailReader *reader)
+{
+	EShell *shell;
+	EShellBackend *shell_backend;
+	EMFormatHTMLDisplay *html_display;
+	EWebView *web_view;
+	CamelURL *curl;
+	const gchar *uri;
+
+	/* This action is defined in EMailDisplay. */
+
+	html_display = e_mail_reader_get_html_display (reader);
+	shell_backend = e_mail_reader_get_shell_backend (reader);
+
+	web_view = E_WEB_VIEW (EM_FORMAT_HTML (html_display)->html);
+
+	uri = e_web_view_get_selected_uri (web_view);
+	g_return_if_fail (uri != NULL);
+
+	curl = camel_url_new (uri, NULL);
+	g_return_if_fail (curl != NULL);
+
+	if (curl->path == NULL || *curl->path == '\0')
+		goto exit;
+
+	/* XXX EBookShellBackend should be listening for this
+	 *     event.  Kind of kludgey, but works for now. */
+	shell = e_shell_backend_get_shell (shell_backend);
+	e_shell_event (shell, "contact-quick-add-email", curl->path);
+
+exit:
+	camel_url_free (curl);
+}
+
+static void
 action_mail_charset_cb (GtkRadioAction *action,
                         GtkRadioAction *current,
                         EMailReader *reader)
@@ -955,7 +995,7 @@ action_search_folder_recipient_cb (GtkAction *action,
 {
 	EMFormatHTMLDisplay *html_display;
 	MessageList *message_list;
-	EMailDisplay *display;
+	EWebView *web_view;
 	CamelURL *curl;
 	const gchar *uri;
 
@@ -964,9 +1004,9 @@ action_search_folder_recipient_cb (GtkAction *action,
 	html_display = e_mail_reader_get_html_display (reader);
 	message_list = e_mail_reader_get_message_list (reader);
 
-	display = E_MAIL_DISPLAY (EM_FORMAT_HTML (html_display)->html);
+	web_view = E_WEB_VIEW (EM_FORMAT_HTML (html_display)->html);
 
-	uri = e_mail_display_get_selected_uri (display);
+	uri = e_web_view_get_selected_uri (web_view);
 	g_return_if_fail (uri != NULL);
 
 	curl = camel_url_new (uri, NULL);
@@ -996,7 +1036,7 @@ action_search_folder_sender_cb (GtkAction *action,
 {
 	EMFormatHTMLDisplay *html_display;
 	MessageList *message_list;
-	EMailDisplay *display;
+	EWebView *web_view;
 	CamelURL *curl;
 	const gchar *uri;
 
@@ -1005,9 +1045,9 @@ action_search_folder_sender_cb (GtkAction *action,
 	html_display = e_mail_reader_get_html_display (reader);
 	message_list = e_mail_reader_get_message_list (reader);
 
-	display = E_MAIL_DISPLAY (EM_FORMAT_HTML (html_display)->html);
+	web_view = E_WEB_VIEW (EM_FORMAT_HTML (html_display)->html);
 
-	uri = e_mail_display_get_selected_uri (display);
+	uri = e_web_view_get_selected_uri (web_view);
 	g_return_if_fail (uri != NULL);
 
 	curl = camel_url_new (uri, NULL);
@@ -2252,7 +2292,7 @@ e_mail_reader_init (EMailReader *reader)
 	EShellBackend *shell_backend;
 	EShellSettings *shell_settings;
 	EMFormatHTMLDisplay *html_display;
-	EMailDisplay *display;
+	EWebView *web_view;
 	GtkActionGroup *action_group;
 	MessageList *message_list;
 	GConfBridge *bridge;
@@ -2270,7 +2310,7 @@ e_mail_reader_init (EMailReader *reader)
 	shell = e_shell_backend_get_shell (shell_backend);
 	shell_settings = e_shell_get_shell_settings (shell);
 
-	display = E_MAIL_DISPLAY (EM_FORMAT_HTML (html_display)->html);
+	web_view = E_WEB_VIEW (EM_FORMAT_HTML (html_display)->html);
 
 	gtk_action_group_add_actions (
 		action_group, mail_reader_entries,
@@ -2320,14 +2360,20 @@ e_mail_reader_init (EMailReader *reader)
 	action = e_mail_reader_get_action (reader, action_name);
 	g_object_set (action, "short-label", _("Reply"), NULL);
 
+	action_name = "add-to-address-book";
+	action = e_web_view_get_action (web_view, action_name);
+	g_signal_connect (
+		action, "activate",
+		G_CALLBACK (action_add_to_address_book_cb), reader);
+
 	action_name = "search-folder-recipient";
-	action = e_mail_display_get_action (display, action_name);
+	action = e_web_view_get_action (web_view, action_name);
 	g_signal_connect (
 		action, "activate",
 		G_CALLBACK (action_search_folder_recipient_cb), reader);
 
 	action_name = "search-folder-sender";
-	action = e_mail_display_get_action (display, action_name);
+	action = e_web_view_get_action (web_view, action_name);
 	g_signal_connect (
 		action, "activate",
 		G_CALLBACK (action_search_folder_sender_cb), reader);
@@ -2350,7 +2396,7 @@ e_mail_reader_init (EMailReader *reader)
 
 	e_binding_new (
 		shell_settings, "mail-show-animated-images",
-		display, "animate");
+		web_view, "animate");
 
 	e_binding_new (
 		shell_settings, "mail-show-sender-photo",
@@ -2361,16 +2407,16 @@ e_mail_reader_init (EMailReader *reader)
 
 	e_mutual_binding_new (
 		action, "active",
-		display, "caret-mode");
+		web_view, "caret-mode");
 
 	/* Connect signals. */
 
 	g_signal_connect_swapped (
-		display, "button-release-event",
+		web_view, "button-release-event",
 		G_CALLBACK (mail_reader_button_release_event_cb), reader);
 
 	g_signal_connect_swapped (
-		display, "key-press-event",
+		web_view, "key-press-event",
 		G_CALLBACK (mail_reader_key_press_event_cb), reader);
 
 	g_signal_connect_swapped (
diff --git a/mail/e-mail-search-bar.c b/mail/e-mail-search-bar.c
index d068eb1..bfd4270 100644
--- a/mail/e-mail-search-bar.c
+++ b/mail/e-mail-search-bar.c
@@ -32,7 +32,7 @@
 	((obj), E_TYPE_MAIL_SEARCH_BAR, EMailSearchBarPrivate))
 
 struct _EMailSearchBarPrivate {
-	GtkHTML *html;
+	EWebView *web_view;
 	GtkWidget *entry;
 	GtkWidget *case_sensitive_button;
 	GtkWidget *wrapped_next_box;
@@ -48,8 +48,8 @@ struct _EMailSearchBarPrivate {
 enum {
 	PROP_0,
 	PROP_CASE_SENSITIVE,
-	PROP_HTML,
-	PROP_TEXT
+	PROP_TEXT,
+	PROP_WEB_VIEW
 };
 
 enum {
@@ -108,14 +108,14 @@ static void
 mail_search_bar_find (EMailSearchBar *search_bar,
                       gboolean search_forward)
 {
-	GtkHTML *html;
+	EWebView *web_view;
 	GtkWidget *widget;
 	gboolean case_sensitive;
 	gboolean new_search;
 	gboolean wrapped = FALSE;
 	gchar *text;
 
-	html = e_mail_search_bar_get_html (search_bar);
+	web_view = e_mail_search_bar_get_web_view (search_bar);
 	case_sensitive = e_mail_search_bar_get_case_sensitive (search_bar);
 	text = e_mail_search_bar_get_text (search_bar);
 
@@ -146,20 +146,23 @@ mail_search_bar_find (EMailSearchBar *search_bar,
 		mail_search_bar_update_tokenizer (search_bar);
 	} else if (search_bar->priv->rerun_search) {
 		gtk_html_engine_search (
-			html, search_bar->priv->active_search,
+			GTK_HTML (web_view),
+			search_bar->priv->active_search,
 			case_sensitive, search_forward, FALSE);
 		search_bar->priv->rerun_search = FALSE;
 		g_free (text);
 	} else {
-		gtk_html_engine_search_set_forward (html, search_forward);
-		if (!gtk_html_engine_search_next (html))
+		gtk_html_engine_search_set_forward (
+			GTK_HTML (web_view), search_forward);
+		if (!gtk_html_engine_search_next (GTK_HTML (web_view)))
 			wrapped = TRUE;
 		g_free (text);
 	}
 
 	if (new_search || wrapped)
 		gtk_html_engine_search (
-			html, search_bar->priv->active_search,
+			GTK_HTML (web_view),
+			search_bar->priv->active_search,
 			case_sensitive, search_forward, FALSE);
 
 	/* Update wrapped label visibility. */
@@ -218,15 +221,17 @@ mail_search_bar_toggled_cb (EMailSearchBar *search_bar)
 }
 
 static void
-mail_search_bar_set_html (EMailSearchBar *search_bar,
-                          GtkHTML *html)
+mail_search_bar_set_web_view (EMailSearchBar *search_bar,
+                              EWebView *web_view)
 {
+	GtkHTML *html;
 	ESearchingTokenizer *tokenizer;
 
-	g_return_if_fail (search_bar->priv->html == NULL);
+	g_return_if_fail (search_bar->priv->web_view == NULL);
 
-	search_bar->priv->html = g_object_ref (html);
+	search_bar->priv->web_view = g_object_ref (web_view);
 
+	html = GTK_HTML (web_view);
 	tokenizer = e_mail_search_bar_get_tokenizer (search_bar);
 	gtk_html_set_tokenizer (html, HTML_TOKENIZER (tokenizer));
 }
@@ -244,17 +249,17 @@ mail_search_bar_set_property (GObject *object,
 				g_value_get_boolean (value));
 			return;
 
-		case PROP_HTML:
-			mail_search_bar_set_html (
-				E_MAIL_SEARCH_BAR (object),
-				g_value_get_object (value));
-			return;
-
 		case PROP_TEXT:
 			e_mail_search_bar_set_text (
 				E_MAIL_SEARCH_BAR (object),
 				g_value_get_string (value));
 			return;
+
+		case PROP_WEB_VIEW:
+			mail_search_bar_set_web_view (
+				E_MAIL_SEARCH_BAR (object),
+				g_value_get_object (value));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -273,17 +278,17 @@ mail_search_bar_get_property (GObject *object,
 				E_MAIL_SEARCH_BAR (object)));
 			return;
 
-		case PROP_HTML:
-			g_value_set_object (
-				value, e_mail_search_bar_get_html (
-				E_MAIL_SEARCH_BAR (object)));
-			return;
-
 		case PROP_TEXT:
 			g_value_take_string (
 				value, e_mail_search_bar_get_text (
 				E_MAIL_SEARCH_BAR (object)));
 			return;
+
+		case PROP_WEB_VIEW:
+			g_value_set_object (
+				value, e_mail_search_bar_get_web_view (
+				E_MAIL_SEARCH_BAR (object)));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -296,9 +301,9 @@ mail_search_bar_dispose (GObject *object)
 
 	priv = E_MAIL_SEARCH_BAR_GET_PRIVATE (object);
 
-	if (priv->html != NULL) {
-		g_object_unref (priv->html);
-		priv->html = NULL;
+	if (priv->web_view != NULL) {
+		g_object_unref (priv->web_view);
+		priv->web_view = NULL;
 	}
 
 	if (priv->entry != NULL) {
@@ -450,17 +455,6 @@ mail_search_bar_class_init (EMailSearchBarClass *class)
 
 	g_object_class_install_property (
 		object_class,
-		PROP_HTML,
-		g_param_spec_object (
-			"html",
-			"HTML Display",
-			NULL,
-			GTK_TYPE_HTML,
-			G_PARAM_READWRITE |
-			G_PARAM_CONSTRUCT_ONLY));
-
-	g_object_class_install_property (
-		object_class,
 		PROP_TEXT,
 		g_param_spec_string (
 			"text",
@@ -469,6 +463,17 @@ mail_search_bar_class_init (EMailSearchBarClass *class)
 			NULL,
 			G_PARAM_READWRITE));
 
+	g_object_class_install_property (
+		object_class,
+		PROP_WEB_VIEW,
+		g_param_spec_object (
+			"web-view",
+			"Web View",
+			NULL,
+			E_TYPE_WEB_VIEW,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY));
+
 	signals[CHANGED] = g_signal_new (
 		"changed",
 		G_TYPE_FROM_CLASS (class),
@@ -676,11 +681,12 @@ e_mail_search_bar_get_type (void)
 }
 
 GtkWidget *
-e_mail_search_bar_new (GtkHTML *html)
+e_mail_search_bar_new (EWebView *web_view)
 {
-	g_return_val_if_fail (GTK_IS_HTML (html), NULL);
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
 
-	return g_object_new (E_TYPE_MAIL_SEARCH_BAR, "html", html, NULL);
+	return g_object_new (
+		E_TYPE_MAIL_SEARCH_BAR, "web-view", web_view, NULL);
 }
 
 void
@@ -699,12 +705,12 @@ e_mail_search_bar_changed (EMailSearchBar *search_bar)
 	g_signal_emit (search_bar, signals[CHANGED], 0);
 }
 
-GtkHTML *
-e_mail_search_bar_get_html (EMailSearchBar *search_bar)
+EWebView *
+e_mail_search_bar_get_web_view (EMailSearchBar *search_bar)
 {
 	g_return_val_if_fail (E_IS_MAIL_SEARCH_BAR (search_bar), NULL);
 
-	return search_bar->priv->html;
+	return search_bar->priv->web_view;
 }
 
 ESearchingTokenizer *
diff --git a/mail/e-mail-search-bar.h b/mail/e-mail-search-bar.h
index f4748c7..1ad3437 100644
--- a/mail/e-mail-search-bar.h
+++ b/mail/e-mail-search-bar.h
@@ -23,8 +23,8 @@
 #define E_MAIL_SEARCH_BAR_H
 
 #include <gtk/gtk.h>
-#include <gtkhtml/gtkhtml.h>
 #include <mail/e-searching-tokenizer.h>
+#include <misc/e-web-view.h>
 
 /* Standard GObject macros */
 #define E_TYPE_MAIL_SEARCH_BAR \
@@ -65,10 +65,10 @@ struct _EMailSearchBarClass {
 };
 
 GType		e_mail_search_bar_get_type	(void);
-GtkWidget *	e_mail_search_bar_new		(GtkHTML *html);
+GtkWidget *	e_mail_search_bar_new		(EWebView *web_view);
 void		e_mail_search_bar_clear		(EMailSearchBar *search_bar);
 void		e_mail_search_bar_changed	(EMailSearchBar *search_bar);
-GtkHTML *	e_mail_search_bar_get_html	(EMailSearchBar *search_bar);
+EWebView *	e_mail_search_bar_get_web_view	(EMailSearchBar *search_bar);
 ESearchingTokenizer *
 		e_mail_search_bar_get_tokenizer	(EMailSearchBar *search_bar);
 gboolean	e_mail_search_bar_get_case_sensitive
diff --git a/mail/em-utils.c b/mail/em-utils.c
index 0a55ea8..4ee9110 100644
--- a/mail/em-utils.c
+++ b/mail/em-utils.c
@@ -48,10 +48,6 @@
 
 #include "em-filter-editor.h"
 
-#include <bonobo/bonobo-listener.h>
-#include <bonobo/bonobo-widget.h>
-#include <bonobo/bonobo-event-source.h>
-
 #include <glib/gi18n.h>
 
 #include <gio/gio.h>
@@ -661,94 +657,6 @@ em_utils_save_messages (GtkWindow *parent, CamelFolder *folder, GPtrArray *uids)
 }
 
 /* ********************************************************************** */
-
-static void
-emu_add_address_cb(BonoboListener *listener, const gchar *name, const CORBA_any *any, CORBA_Environment *ev, gpointer data)
-{
-	gchar *type = bonobo_event_subtype(name);
-
-	if (!strcmp(type, "Destroy"))
-		gtk_widget_destroy((GtkWidget *)data);
-
-	g_free(type);
-}
-
-/* one of email or vcard should be always NULL, never both of them */
-static void
-emu_add_address_or_vcard (GtkWindow *parent, const gchar *email, const gchar *vcard)
-{
-	GtkWidget *win;
-	GtkWidget *control;
-	/*GtkWidget *socket;*/
-	gchar *email_buf = NULL;
-
-	if (email) {
-		CamelInternetAddress *cia;
-
-		cia = camel_internet_address_new ();
-		if (camel_address_decode ((CamelAddress *) cia, email) == -1) {
-			camel_object_unref (cia);
-			return;
-		}
-
-		email_buf = camel_address_format ((CamelAddress *) cia);
-		camel_object_unref (cia);
-	}
-
-	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_title((GtkWindow *)win, _("Add address"));
-
-	gtk_window_set_transient_for ((GtkWindow *)win, parent);
-
-	gtk_window_set_position((GtkWindow *)win, GTK_WIN_POS_CENTER_ON_PARENT);
-	gtk_window_set_type_hint((GtkWindow *)win, GDK_WINDOW_TYPE_HINT_DIALOG);
-
-	control = bonobo_widget_new_control("OAFIID:GNOME_Evolution_Addressbook_AddressPopup:" BASE_VERSION, CORBA_OBJECT_NIL);
-
-	if (email_buf)
-		bonobo_widget_set_property ((BonoboWidget *) control, "email", TC_CORBA_string, email_buf, NULL);
-	else
-		bonobo_widget_set_property ((BonoboWidget *) control, "vcard", TC_CORBA_string, vcard, NULL);
-
-	g_free (email_buf);
-
-	bonobo_event_source_client_add_listener(bonobo_widget_get_objref((BonoboWidget *)control), emu_add_address_cb, NULL, NULL, win);
-
-	/*socket = find_socket (GTK_CONTAINER (control));
-	  g_object_weak_ref ((GObject *) socket, (GWeakNotify) gtk_widget_destroy, win);*/
-
-	gtk_container_add((GtkContainer *)win, control);
-	gtk_widget_show_all(win);
-}
-
-/**
- * em_utils_add_address:
- * @parent:
- * @email:
- *
- * Add address @email to the addressbook.
- **/
-void
-em_utils_add_address (GtkWindow *parent, const gchar *email)
-{
-	g_return_if_fail (GTK_IS_WINDOW (parent));
-
-	emu_add_address_or_vcard (parent, email, NULL);
-}
-
-/**
- * em_utils_add_vcard:
- * Adds whole vCard to the addressbook.
- **/
-void
-em_utils_add_vcard (GtkWindow *parent, const gchar *vcard)
-{
-	g_return_if_fail (GTK_IS_WINDOW (parent));
-
-	emu_add_address_or_vcard (parent, NULL, vcard);
-}
-
-/* ********************************************************************** */
 /* Flag-for-Followup... */
 
 /**
diff --git a/mail/em-utils.h b/mail/em-utils.h
index 14dc8ce..e1064c5 100644
--- a/mail/em-utils.h
+++ b/mail/em-utils.h
@@ -56,9 +56,6 @@ void em_utils_save_part(GtkWindow *parent, const gchar *prompt, CamelMimePart *p
 gboolean em_utils_save_part_to_file(GtkWindow *parent, const gchar *filename, CamelMimePart *part);
 void em_utils_save_messages (GtkWindow *parent, CamelFolder *folder, GPtrArray *uids);
 
-void em_utils_add_address(GtkWindow *parent, const gchar *email);
-void em_utils_add_vcard(GtkWindow *parent, const gchar *vcard);
-
 void em_utils_flag_for_followup (EMailReader *reader, CamelFolder *folder, GPtrArray *uids);
 void em_utils_flag_for_followup_clear (GtkWindow *parent, CamelFolder *folder, GPtrArray *uids);
 void em_utils_flag_for_followup_completed (GtkWindow *parent, CamelFolder *folder, GPtrArray *uids);
diff --git a/modules/addressbook/e-book-shell-backend.c b/modules/addressbook/e-book-shell-backend.c
index 243ce3a..a14b3ba 100644
--- a/modules/addressbook/e-book-shell-backend.c
+++ b/modules/addressbook/e-book-shell-backend.c
@@ -37,6 +37,7 @@
 
 #include "addressbook/gui/widgets/eab-gui-util.h"
 #include "addressbook/gui/contact-editor/e-contact-editor.h"
+#include "addressbook/gui/contact-editor/e-contact-quick-add.h"
 #include "addressbook/gui/contact-list-editor/e-contact-list-editor.h"
 #include "addressbook/importers/evolution-addressbook-importers.h"
 
@@ -379,6 +380,26 @@ book_shell_backend_init_preferences (EShell *shell)
 	return FALSE;
 }
 
+static void
+book_shell_backend_quick_add_email_cb (EShell *shell,
+                                       const gchar *email)
+{
+	/* XXX This is an ugly hack but it's the only way I could think
+	 *     of to integrate this feature with other shell modules. */
+
+	e_contact_quick_add_free_form (email, NULL, NULL);
+}
+
+static void
+book_shell_backend_quick_add_vcard_cb (EShell *shell,
+                                       const gchar *vcard)
+{
+	/* XXX This is an ugly hack but it's the only way I could think
+	 *     of to integrate this feature with other shell modules. */
+
+	e_contact_quick_add_vcard (vcard, NULL, NULL);
+}
+
 static gboolean
 book_shell_backend_handle_uri_cb (EShellBackend *shell_backend,
                                   const gchar *uri)
@@ -518,6 +539,14 @@ book_shell_backend_constructed (GObject *object)
 	book_shell_backend_init_importers ();
 	book_shell_backend_ensure_sources (shell_backend);
 
+	g_signal_connect (
+		shell, "event::contact-quick-add-email",
+		G_CALLBACK (book_shell_backend_quick_add_email_cb), NULL);
+
+	g_signal_connect_swapped (
+		shell, "event::contact-quick-add-vcard",
+		G_CALLBACK (book_shell_backend_quick_add_vcard_cb), NULL);
+
 	g_signal_connect_swapped (
 		shell, "handle-uri",
 		G_CALLBACK (book_shell_backend_handle_uri_cb),
diff --git a/modules/calendar/e-memo-shell-content.c b/modules/calendar/e-memo-shell-content.c
index 027beec..0e8d73b 100644
--- a/modules/calendar/e-memo-shell-content.c
+++ b/modules/calendar/e-memo-shell-content.c
@@ -200,7 +200,7 @@ memo_shell_content_cursor_change_cb (EMemoShellContent *memo_shell_content,
 	memo_preview = e_memo_shell_content_get_memo_preview (memo_shell_content);
 
 	if (e_table_selected_count (table) != 1) {
-		e_cal_component_preview_clear (memo_preview);
+		e_web_view_clear (E_WEB_VIEW (memo_preview));
 		return;
 	}
 
@@ -231,7 +231,7 @@ memo_shell_content_selection_change_cb (EMemoShellContent *memo_shell_content,
 	/* XXX Old code emits a "selection-changed" signal here. */
 
 	if (e_table_selected_count (table) != 1)
-		e_cal_component_preview_clear (memo_preview);
+		e_web_view_clear (E_WEB_VIEW (memo_preview));
 }
 
 static void
@@ -391,6 +391,7 @@ memo_shell_content_constructed (GObject *object)
 	EShellSettings *shell_settings;
 	EShellBackend *shell_backend;
 	EShellContent *shell_content;
+	EShellTaskbar *shell_taskbar;
 	GalViewInstance *view_instance;
 	icaltimezone *timezone;
 	ETable *table;
@@ -407,6 +408,7 @@ memo_shell_content_constructed (GObject *object)
 	shell_content = E_SHELL_CONTENT (object);
 	shell_view = e_shell_content_get_shell_view (shell_content);
 	shell_backend = e_shell_view_get_shell_backend (shell_view);
+	shell_taskbar = e_shell_view_get_shell_taskbar (shell_view);
 
 	shell = e_shell_backend_get_shell (shell_backend);
 	shell_settings = e_shell_get_shell_settings (shell);
@@ -458,6 +460,11 @@ memo_shell_content_constructed (GObject *object)
 	priv->memo_preview = g_object_ref (widget);
 	gtk_widget_show (widget);
 
+	g_signal_connect_swapped (
+		widget, "status-message",
+		G_CALLBACK (e_shell_taskbar_set_message),
+		shell_taskbar);
+
 	/* Configure the memo table. */
 
 	widget = E_MEMO_TABLE (priv->memo_table)->etable;
diff --git a/modules/calendar/e-memo-shell-view-actions.c b/modules/calendar/e-memo-shell-view-actions.c
index 99761b2..463e01f 100644
--- a/modules/calendar/e-memo-shell-view-actions.c
+++ b/modules/calendar/e-memo-shell-view-actions.c
@@ -97,7 +97,7 @@ action_memo_delete_cb (GtkAction *action,
 	e_memo_table_delete_selected (memo_table);
 	e_memo_shell_view_set_status_message (memo_shell_view, NULL, -1.0);
 
-	e_cal_component_preview_clear (memo_preview);
+	e_web_view_clear (E_WEB_VIEW (memo_preview));
 }
 
 static void
diff --git a/modules/calendar/e-memo-shell-view-private.c b/modules/calendar/e-memo-shell-view-private.c
index 0a96d29..f1d331f 100644
--- a/modules/calendar/e-memo-shell-view-private.c
+++ b/modules/calendar/e-memo-shell-view-private.c
@@ -24,24 +24,6 @@
 #include "widgets/menus/gal-view-factory-etable.h"
 
 static void
-memo_shell_view_preview_on_url_cb (EShellView *shell_view,
-                                   const gchar *url)
-{
-	EShellTaskbar *shell_taskbar;
-	gchar *message;
-
-	shell_taskbar = e_shell_view_get_shell_taskbar (shell_view);
-
-	if (url == NULL || *url == '\0')
-		e_shell_taskbar_set_message (shell_taskbar, NULL);
-	else {
-		message = g_strdup_printf (_("Click to open %s"), url);
-		e_shell_taskbar_set_message (shell_taskbar, message);
-		g_free (message);
-	}
-}
-
-static void
 memo_shell_view_table_popup_event_cb (EShellView *shell_view,
                                       GdkEventButton *event)
 {
@@ -189,7 +171,6 @@ e_memo_shell_view_private_constructed (EMemoShellView *memo_shell_view)
 	EShellContent *shell_content;
 	EShellSidebar *shell_sidebar;
 	EShellWindow *shell_window;
-	ECalComponentPreview *memo_preview;
 	EMemoTable *memo_table;
 	ECalModel *model;
 	ETable *table;
@@ -210,7 +191,6 @@ e_memo_shell_view_private_constructed (EMemoShellView *memo_shell_view)
 	priv->memo_shell_sidebar = g_object_ref (shell_sidebar);
 
 	memo_shell_content = E_MEMO_SHELL_CONTENT (shell_content);
-	memo_preview = e_memo_shell_content_get_memo_preview (memo_shell_content);
 	memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
 	model = e_memo_table_get_model (memo_table);
 	table = e_memo_table_get_table (memo_table);
@@ -224,11 +204,6 @@ e_memo_shell_view_private_constructed (EMemoShellView *memo_shell_view)
 		memo_shell_view);
 
 	g_signal_connect_swapped (
-		memo_preview, "on-url",
-		G_CALLBACK (memo_shell_view_preview_on_url_cb),
-		memo_shell_view);
-
-	g_signal_connect_swapped (
 		memo_table, "open-component",
 		G_CALLBACK (e_memo_shell_view_open_memo),
 		memo_shell_view);
diff --git a/modules/calendar/e-memo-shell-view.c b/modules/calendar/e-memo-shell-view.c
index 54dea9c..ea869b5 100644
--- a/modules/calendar/e-memo-shell-view.c
+++ b/modules/calendar/e-memo-shell-view.c
@@ -151,7 +151,7 @@ memo_shell_view_execute_search (EShellView *shell_view)
 
 	memo_preview =
 		e_memo_shell_content_get_memo_preview (memo_shell_content);
-	e_cal_component_preview_clear (memo_preview);
+	e_web_view_clear (E_WEB_VIEW (memo_preview));
 }
 
 static void
diff --git a/modules/calendar/e-task-shell-content.c b/modules/calendar/e-task-shell-content.c
index f78f142..8dea4b8 100644
--- a/modules/calendar/e-task-shell-content.c
+++ b/modules/calendar/e-task-shell-content.c
@@ -201,7 +201,7 @@ task_shell_content_cursor_change_cb (ETaskShellContent *task_shell_content,
 	task_preview = e_task_shell_content_get_task_preview (task_shell_content);
 
 	if (e_table_selected_count (table) != 1) {
-		e_cal_component_preview_clear (task_preview);
+		e_web_view_clear (E_WEB_VIEW (task_preview));
 		return;
 	}
 
@@ -230,7 +230,7 @@ task_shell_content_selection_change_cb (ETaskShellContent *task_shell_content,
 	task_preview = e_task_shell_content_get_task_preview (task_shell_content);
 
 	if (e_table_selected_count (table) != 1)
-		e_cal_component_preview_clear (task_preview);
+		e_web_view_clear (E_WEB_VIEW (task_preview));
 }
 
 static void
@@ -388,6 +388,7 @@ task_shell_content_constructed (GObject *object)
 	EShell *shell;
 	EShellSettings *shell_settings;
 	EShellContent *shell_content;
+	EShellTaskbar *shell_taskbar;
 	EShellWindow *shell_window;
 	EShellView *shell_view;
 	GalViewInstance *view_instance;
@@ -405,6 +406,7 @@ task_shell_content_constructed (GObject *object)
 
 	shell_content = E_SHELL_CONTENT (object);
 	shell_view = e_shell_content_get_shell_view (shell_content);
+	shell_taskbar = e_shell_view_get_shell_taskbar (shell_view);
 	shell_window = e_shell_view_get_shell_window (shell_view);
 	shell = e_shell_window_get_shell (shell_window);
 	shell_settings = e_shell_get_shell_settings (shell);
@@ -456,6 +458,11 @@ task_shell_content_constructed (GObject *object)
 	priv->task_preview = g_object_ref (widget);
 	gtk_widget_show (widget);
 
+	g_signal_connect_swapped (
+		widget, "status-message",
+		G_CALLBACK (e_shell_taskbar_set_message),
+		shell_taskbar);
+
 	/* Configure the task table. */
 
 	widget = E_CALENDAR_TABLE (priv->task_table)->etable;
diff --git a/modules/calendar/e-task-shell-view-actions.c b/modules/calendar/e-task-shell-view-actions.c
index 7833873..2592df2 100644
--- a/modules/calendar/e-task-shell-view-actions.c
+++ b/modules/calendar/e-task-shell-view-actions.c
@@ -128,7 +128,7 @@ action_task_delete_cb (GtkAction *action,
 	e_calendar_table_delete_selected (task_table);
 	e_task_shell_view_set_status_message (task_shell_view, NULL, -1.0);
 
-	e_cal_component_preview_clear (task_preview);
+	e_web_view_clear (E_WEB_VIEW (task_preview));
 }
 
 static void
diff --git a/modules/calendar/e-task-shell-view-private.c b/modules/calendar/e-task-shell-view-private.c
index 380d54e..581ad88 100644
--- a/modules/calendar/e-task-shell-view-private.c
+++ b/modules/calendar/e-task-shell-view-private.c
@@ -47,24 +47,6 @@ task_shell_view_process_completed_tasks (ETaskShellView *task_shell_view)
 }
 
 static void
-task_shell_view_preview_on_url_cb (EShellView *shell_view,
-                                   const gchar *url)
-{
-	EShellTaskbar *shell_taskbar;
-	gchar *message;
-
-	shell_taskbar = e_shell_view_get_shell_taskbar (shell_view);
-
-	if (url == NULL || *url == '\0')
-		e_shell_taskbar_set_message (shell_taskbar, NULL);
-	else {
-		message = g_strdup_printf (_("Click to open %s"), url);
-		e_shell_taskbar_set_message (shell_taskbar, message);
-		g_free (message);
-	}
-}
-
-static void
 task_shell_view_table_popup_event_cb (EShellView *shell_view,
                                       GdkEventButton *event)
 {
@@ -235,10 +217,10 @@ e_task_shell_view_private_constructed (ETaskShellView *task_shell_view)
 	EShellView *shell_view;
 	EShellBackend *shell_backend;
 	EShellContent *shell_content;
-	EShellSettings *shell_settings;
 	EShellSidebar *shell_sidebar;
+	EShellTaskbar *shell_taskbar;
+	EShellSettings *shell_settings;
 	EShellWindow *shell_window;
-	ECalComponentPreview *task_preview;
 	ECalendarTable *task_table;
 	ECalModel *model;
 	ETable *table;
@@ -248,6 +230,7 @@ e_task_shell_view_private_constructed (ETaskShellView *task_shell_view)
 	shell_backend = e_shell_view_get_shell_backend (shell_view);
 	shell_content = e_shell_view_get_shell_content (shell_view);
 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+	shell_taskbar = e_shell_view_get_shell_taskbar (shell_view);
 	shell_window = e_shell_view_get_shell_window (shell_view);
 
 	shell = e_shell_window_get_shell (shell_window);
@@ -262,7 +245,6 @@ e_task_shell_view_private_constructed (ETaskShellView *task_shell_view)
 	priv->task_shell_sidebar = g_object_ref (shell_sidebar);
 
 	task_shell_content = E_TASK_SHELL_CONTENT (shell_content);
-	task_preview = e_task_shell_content_get_task_preview (task_shell_content);
 	task_table = e_task_shell_content_get_task_table (task_shell_content);
 	model = e_calendar_table_get_model (task_table);
 	table = e_calendar_table_get_table (task_table);
@@ -276,11 +258,6 @@ e_task_shell_view_private_constructed (ETaskShellView *task_shell_view)
 		task_shell_view);
 
 	g_signal_connect_swapped (
-		task_preview, "on-url",
-		G_CALLBACK (task_shell_view_preview_on_url_cb),
-		task_shell_view);
-
-	g_signal_connect_swapped (
 		task_table, "open-component",
 		G_CALLBACK (e_task_shell_view_open_task),
 		task_shell_view);
diff --git a/modules/calendar/e-task-shell-view.c b/modules/calendar/e-task-shell-view.c
index 7237b1c..650037b 100644
--- a/modules/calendar/e-task-shell-view.c
+++ b/modules/calendar/e-task-shell-view.c
@@ -263,7 +263,7 @@ task_shell_view_execute_search (EShellView *shell_view)
 
 	task_preview =
 		e_task_shell_content_get_task_preview (task_shell_content);
-	e_cal_component_preview_clear (task_preview);
+	e_web_view_clear (E_WEB_VIEW (task_preview));
 }
 
 static void
diff --git a/modules/mail/e-mail-shell-content.c b/modules/mail/e-mail-shell-content.c
index ac740fb..60ddde7 100644
--- a/modules/mail/e-mail-shell-content.c
+++ b/modules/mail/e-mail-shell-content.c
@@ -438,7 +438,7 @@ mail_shell_content_constructed (GObject *object)
 	GConfBridge *bridge;
 	GtkWidget *container;
 	GtkWidget *widget;
-	GtkHTML *html;
+	EWebView *web_view;
 	GalViewCollection *view_collection;
 	const gchar *key;
 
@@ -454,7 +454,7 @@ mail_shell_content_constructed (GObject *object)
 	shell_backend = e_shell_view_get_shell_backend (shell_view);
 	view_collection = shell_view_class->view_collection;
 
-	html = EM_FORMAT_HTML (priv->html_display)->html;
+	web_view = E_WEB_VIEW (EM_FORMAT_HTML (priv->html_display)->html);
 
 	/* Build content widgets. */
 
@@ -492,12 +492,12 @@ mail_shell_content_constructed (GObject *object)
 		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 	gtk_scrolled_window_set_shadow_type (
 		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
-	gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (html));
+	gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (web_view));
 	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
-	gtk_widget_show (GTK_WIDGET (html));
+	gtk_widget_show (GTK_WIDGET (web_view));
 	gtk_widget_show (widget);
 
-	widget = e_mail_search_bar_new (html);
+	widget = e_mail_search_bar_new (web_view);
 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 	priv->search_bar = g_object_ref (widget);
 	gtk_widget_hide (widget);
diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c
index adb288d..9c79164 100644
--- a/modules/mail/e-mail-shell-view-actions.c
+++ b/modules/mail/e-mail-shell-view-actions.c
@@ -681,7 +681,7 @@ action_mail_smart_backward_cb (GtkAction *action,
 
 	html = EM_FORMAT_HTML (html_display)->html;
 
-	if (gtk_html_command (html, "scroll-backward"))
+	if (e_web_view_scroll_backward (E_WEB_VIEW (html)))
 		return;
 
 	if (caret_mode || !magic_spacebar)
@@ -744,7 +744,7 @@ action_mail_smart_forward_cb (GtkAction *action,
 
 	html = EM_FORMAT_HTML (html_display)->html;
 
-	if (gtk_html_command (html, "scroll-forward"))
+	if (e_web_view_scroll_forward (E_WEB_VIEW (html)))
 		return;
 
 	if (caret_mode || !magic_spacebar)
diff --git a/modules/mail/e-mail-shell-view-private.h b/modules/mail/e-mail-shell-view-private.h
index 30724b2..5c52e12 100644
--- a/modules/mail/e-mail-shell-view-private.h
+++ b/modules/mail/e-mail-shell-view-private.h
@@ -36,6 +36,7 @@
 #include "e-util/gconf-bridge.h"
 #include "e-util/e-account-utils.h"
 #include "filter/filter-part.h"
+#include "widgets/misc/e-web-view.h"
 #include "widgets/misc/e-popup-action.h"
 #include "widgets/menus/gal-view-instance.h"
 
diff --git a/plugins/vcard-inline/Makefile.am b/plugins/vcard-inline/Makefile.am
index dd8f92f..a126750 100644
--- a/plugins/vcard-inline/Makefile.am
+++ b/plugins/vcard-inline/Makefile.am
@@ -6,8 +6,9 @@ NO_UNDEFINED_REQUIRED_LIBS =				\
 endif
 
 AM_CPPFLAGS = \
-	-I$(top_srcdir)			\
-	$(EVOLUTION_ADDRESSBOOK_CFLAGS)	\
+	-I$(top_srcdir)					\
+	-I$(top_srcdir)/widgets				\
+	$(EVOLUTION_ADDRESSBOOK_CFLAGS)			\
 	$(EVOLUTION_MAIL_CFLAGS)
 
 @EVO_PLUGIN_RULE@
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5d63101..ec74fd8 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -387,9 +387,9 @@ plugins/mail-notification/mail-notification.c
 plugins/mail-notification/org-gnome-mail-notification.eplug.xml
 plugins/mail-to-task/mail-to-task.c
 plugins/mail-to-task/org-gnome-mail-to-task.eplug.xml
+plugins/mailing-list-actions/mailing-list-actions.c
 plugins/mailing-list-actions/org-gnome-mailing-list-actions.eplug.xml
 plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml
-plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml
 plugins/mark-all-read/mark-all-read.c
 plugins/mark-all-read/org-gnome-mark-all-read.eplug.xml
 plugins/plugin-manager/org-gnome-plugin-manager.eplug.xml
@@ -505,6 +505,7 @@ widgets/misc/e-signature-editor.c
 widgets/misc/e-signature-manager.c
 widgets/misc/e-signature-script-dialog.c
 widgets/misc/e-url-entry.c
+widgets/misc/e-web-view.c
 widgets/misc/ea-calendar-item.c
 widgets/table/e-cell-combo.c
 widgets/table/e-cell-date-edit.c
diff --git a/shell/e-shell-view.c b/shell/e-shell-view.c
index f580b1a..0bfa17c 100644
--- a/shell/e-shell-view.c
+++ b/shell/e-shell-view.c
@@ -401,6 +401,12 @@ shell_view_constructed (GObject *object)
 
 	/* Invoke factory methods. */
 
+	/* Create the taskbar widget first so the content and
+	 * sidebar widgets can access it during construction. */
+	widget = shell_view_class->new_shell_taskbar (shell_view);
+	shell_view->priv->shell_taskbar = g_object_ref_sink (widget);
+	gtk_widget_show (widget);
+
 	widget = shell_view_class->new_shell_content (shell_view);
 	shell_view->priv->shell_content = g_object_ref_sink (widget);
 	gtk_widget_show (widget);
@@ -409,10 +415,6 @@ shell_view_constructed (GObject *object)
 	shell_view->priv->shell_sidebar = g_object_ref_sink (widget);
 	gtk_widget_show (widget);
 
-	widget = shell_view_class->new_shell_taskbar (shell_view);
-	shell_view->priv->shell_taskbar = g_object_ref_sink (widget);
-	gtk_widget_show (widget);
-
 	/* Size group should be safe to unreference now. */
 	g_object_unref (shell_view->priv->size_group);
 	shell_view->priv->size_group = NULL;
diff --git a/shell/e-shell-window-private.c b/shell/e-shell-window-private.c
index 2cf24ba..2934a82 100644
--- a/shell/e-shell-window-private.c
+++ b/shell/e-shell-window-private.c
@@ -107,23 +107,20 @@ shell_window_init_switcher_style (EShellWindow *shell_window)
 
 static void
 shell_window_menu_item_select_cb (EShellWindow *shell_window,
-                                  GtkWidget *menu_item)
+                                  GtkWidget *widget)
 {
 	GtkAction *action;
 	GtkLabel *label;
-	gchar *tooltip = NULL;
+	const gchar *tooltip;
 
-	action = g_object_get_data (G_OBJECT (menu_item), "action");
-	g_return_if_fail (GTK_IS_ACTION (action));
-
-	g_object_get (action, "tooltip", &tooltip, NULL);
+	action = gtk_widget_get_action (widget);
+	tooltip = gtk_action_get_tooltip (action);
 
 	if (tooltip == NULL)
 		return;
 
 	label = GTK_LABEL (shell_window->priv->tooltip_label);
 	gtk_label_set_text (label, tooltip);
-	g_free (tooltip);
 
 	gtk_widget_show (shell_window->priv->tooltip_label);
 	gtk_widget_hide (shell_window->priv->status_notebook);
@@ -144,11 +141,6 @@ shell_window_connect_proxy_cb (EShellWindow *shell_window,
 	if (!GTK_IS_MENU_ITEM (proxy))
 		return;
 
-	g_object_set_data_full (
-		G_OBJECT (proxy),
-		"action", g_object_ref (action),
-		(GDestroyNotify) g_object_unref);
-
 	g_signal_connect_swapped (
 		proxy, "select",
 		G_CALLBACK (shell_window_menu_item_select_cb),
diff --git a/shell/e-shell.c b/shell/e-shell.c
index d2a8267..bf66464 100644
--- a/shell/e-shell.c
+++ b/shell/e-shell.c
@@ -371,7 +371,7 @@ shell_load_modules (EShell *shell)
 	}
 }
 
-/* Helper for shell_process_backend() */
+/* Helper for shell_add_backend() */
 static void
 shell_split_and_insert_items (GHashTable *hash_table,
                               const gchar *items,
@@ -392,14 +392,17 @@ shell_split_and_insert_items (GHashTable *hash_table,
 }
 
 static void
-shell_process_backend (EShell *shell,
-                       EShellBackend *shell_backend)
+shell_add_backend (GType type,
+                   EShell *shell)
 {
 	EShellBackendClass *class;
+	EShellBackend *shell_backend;
 	GHashTable *backends_by_name;
 	GHashTable *backends_by_scheme;
 	const gchar *string;
 
+	shell_backend = g_object_new (type, "shell", shell, NULL);
+
 	shell->priv->loaded_backends = g_list_insert_sorted (
 		shell->priv->loaded_backends, shell_backend,
 		(GCompareFunc) e_shell_backend_compare);
@@ -425,26 +428,6 @@ shell_process_backend (EShell *shell,
 }
 
 static void
-shell_create_backends (EShell *shell)
-{
-	GType *children;
-	guint ii, n_children;
-
-	/* Create an instance of each EShellBackend subclass. */
-	children = g_type_children (E_TYPE_SHELL_BACKEND, &n_children);
-
-	for (ii = 0; ii < n_children; ii++) {
-		EShellBackend *shell_backend;
-		GType type = children[ii];
-
-		shell_backend = g_object_new (type, "shell", shell, NULL);
-		shell_process_backend (shell, shell_backend);
-	}
-
-	g_free (children);
-}
-
-static void
 shell_sm_quit_requested_cb (EShell *shell,
                             EggSMClient *sm_client)
 {
@@ -596,7 +579,10 @@ shell_constructed (GObject *object)
 	e_file_lock_create ();
 
 	shell_load_modules (E_SHELL (object));
-	shell_create_backends (E_SHELL (object));
+
+	e_type_traverse (
+		E_TYPE_SHELL_BACKEND, (ETypeFunc)
+		shell_add_backend, object);
 }
 
 static gboolean
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am
index 2960775..91906b0 100644
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@ -86,6 +86,7 @@ widgetsinclude_HEADERS =			\
 	e-spinner.h				\
 	e-timeout-activity.h			\
 	e-url-entry.h				\
+	e-web-view.h				\
 	ea-calendar-cell.h			\
 	ea-calendar-item.h			\
 	ea-cell-table.h				\
@@ -148,6 +149,7 @@ libemiscwidgets_la_SOURCES =			\
 	e-signature-tree-view.c			\
 	e-timeout-activity.c			\
 	e-url-entry.c				\
+	e-web-view.c				\
 	ea-calendar-cell.c			\
 	ea-calendar-item.c			\
 	ea-cell-table.c				\
diff --git a/widgets/misc/e-attachment-view.c b/widgets/misc/e-attachment-view.c
index 17a2b6c..cd5301a 100644
--- a/widgets/misc/e-attachment-view.c
+++ b/widgets/misc/e-attachment-view.c
@@ -682,12 +682,30 @@ attachment_view_update_actions (EAttachmentView *view)
 }
 
 static void
+attachment_view_add_handler (GType type,
+                             EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+	EAttachmentHandler *handler;
+	const GtkTargetEntry *targets;
+	guint n_targets;
+
+	priv = e_attachment_view_get_private (view);
+
+	handler = g_object_new (type, "view", view, NULL);
+
+	targets = e_attachment_handler_get_target_table (handler, &n_targets);
+	gtk_target_list_add_table (priv->target_list, targets, n_targets);
+	priv->drag_actions = e_attachment_handler_get_drag_actions (handler);
+
+	g_ptr_array_add (priv->handlers, handler);
+}
+
+static void
 attachment_view_init_handlers (EAttachmentView *view)
 {
 	EAttachmentViewPrivate *priv;
 	GtkTargetList *target_list;
-	GType *children;
-	guint ii;
 
 	priv = e_attachment_view_get_private (view);
 
@@ -700,25 +718,9 @@ attachment_view_init_handlers (EAttachmentView *view)
 	priv->target_list = target_list;
 	priv->drag_actions = GDK_ACTION_COPY;
 
-	children = g_type_children (E_TYPE_ATTACHMENT_HANDLER, NULL);
-
-	for (ii = 0; children[ii] != G_TYPE_INVALID; ii++) {
-		EAttachmentHandler *handler;
-		const GtkTargetEntry *targets;
-		guint n_targets;
-
-		handler = g_object_new (children[ii], "view", view, NULL);
-
-		targets = e_attachment_handler_get_target_table (
-			handler, &n_targets);
-		gtk_target_list_add_table (target_list, targets, n_targets);
-		priv->drag_actions |=
-			e_attachment_handler_get_drag_actions (handler);
-
-		g_ptr_array_add (priv->handlers, handler);
-	}
-
-	g_free (children);
+	e_type_traverse (
+		E_TYPE_ATTACHMENT_HANDLER, (ETypeFunc)
+		attachment_view_add_handler, view);
 }
 
 static void
diff --git a/widgets/misc/e-signature-preview.c b/widgets/misc/e-signature-preview.c
index bbef65f..a9316e4 100644
--- a/widgets/misc/e-signature-preview.c
+++ b/widgets/misc/e-signature-preview.c
@@ -113,56 +113,17 @@ signature_preview_dispose (GObject *object)
 }
 
 static void
-signature_preview_url_requested (GtkHTML *html,
-                                 const gchar *url,
-                                 GtkHTMLStream *handle)
-{
-	GtkHTMLStreamStatus status;
-	gchar buffer[128];
-	gchar *filename;
-	gssize size;
-	gint fd;
-
-	/* FIXME Use GInputStream for this. */
-
-	if (g_str_has_prefix (url, "file:"))
-		filename = g_filename_from_uri (url, NULL, NULL);
-	else
-		filename = g_strdup (url);
-	fd = g_open (filename, O_RDONLY, 0);
-	g_free (filename);
-
-	status = GTK_HTML_STREAM_OK;
-	if (fd != -1) {
-		while ((size = read (fd, buffer, sizeof (buffer)))) {
-			if (size == -1) {
-				status = GTK_HTML_STREAM_ERROR;
-				break;
-			} else
-				gtk_html_write (html, handle, buffer, size);
-		}
-	} else
-		status = GTK_HTML_STREAM_ERROR;
-
-	gtk_html_end (html, handle, status);
-
-	if (fd > 0)
-		close (fd);
-}
-
-static void
 signature_preview_refresh (ESignaturePreview *preview)
 {
-	GtkHTML *html;
+	EWebView *web_view;
 	ESignature *signature;
 	const gchar *filename;
 	gboolean is_script;
 	gchar *content = NULL;
-	gsize length;
 
 	/* XXX We should show error messages in the preview. */
 
-	html = GTK_HTML (preview);
+	web_view = E_WEB_VIEW (preview);
 	signature = e_signature_preview_get_signature (preview);
 
 	if (signature == NULL)
@@ -182,27 +143,23 @@ signature_preview_refresh (ESignaturePreview *preview)
 	if (content == NULL || *content == '\0')
 		goto clear;
 
-	length = strlen (content);
-
 	if (e_signature_get_is_html (signature))
-		gtk_html_load_from_string (html, content, length);
+		e_web_view_load_string (web_view, content);
 	else {
-		GtkHTMLStream *stream;
-
-		stream = gtk_html_begin_content (
-			html, "text/html; charset=utf-8");
-		gtk_html_write (html, stream, "<PRE>", 5);
-		if (length > 0)
-			gtk_html_write (html, stream, content, length);
-		gtk_html_write (html, stream, "</PRE>", 6);
-		gtk_html_end (html, stream, GTK_HTML_STREAM_OK);
+		gchar *string;
+
+		string = g_strdup_printf ("<PRE>%s</PRE>", content);
+		e_web_view_load_string (web_view, string);
+		g_free (string);
 	}
 
 	g_free (content);
+
 	return;
 
 clear:
-	gtk_html_load_from_string (html, " ", 1);
+	e_web_view_clear (web_view);
+
 	g_free (content);
 }
 
@@ -210,7 +167,6 @@ static void
 signature_preview_class_init (ESignaturePreviewClass *class)
 {
 	GObjectClass *object_class;
-	GtkHTMLClass *html_class;
 
 	parent_class = g_type_class_peek_parent (class);
 	g_type_class_add_private (class, sizeof (ESignaturePreviewPrivate));
@@ -220,9 +176,6 @@ signature_preview_class_init (ESignaturePreviewClass *class)
 	object_class->get_property = signature_preview_get_property;
 	object_class->dispose = signature_preview_dispose;
 
-	html_class = GTK_HTML_CLASS (class);
-	html_class->url_requested = signature_preview_url_requested;
-
 	class->refresh = signature_preview_refresh;
 
 	g_object_class_install_property (
@@ -282,7 +235,7 @@ e_signature_preview_get_type (void)
 		};
 
 		type = g_type_register_static (
-			GTK_TYPE_HTML, "ESignaturePreview", &type_info, 0);
+			E_TYPE_WEB_VIEW, "ESignaturePreview", &type_info, 0);
 	}
 
 	return type;
diff --git a/widgets/misc/e-signature-preview.h b/widgets/misc/e-signature-preview.h
index a422183..d0d5c22 100644
--- a/widgets/misc/e-signature-preview.h
+++ b/widgets/misc/e-signature-preview.h
@@ -22,8 +22,8 @@
 #ifndef E_SIGNATURE_PREVIEW_H
 #define E_SIGNATURE_PREVIEW_H
 
-#include <gtkhtml/gtkhtml.h>
 #include <e-util/e-signature.h>
+#include <misc/e-web-view.h>
 
 /* Standard GObject macros */
 #define E_TYPE_SIGNATURE_PREVIEW \
@@ -51,12 +51,12 @@ typedef struct _ESignaturePreviewClass ESignaturePreviewClass;
 typedef struct _ESignaturePreviewPrivate ESignaturePreviewPrivate;
 
 struct _ESignaturePreview {
-	GtkHTML parent;
+	EWebView parent;
 	ESignaturePreviewPrivate *priv;
 };
 
 struct _ESignaturePreviewClass {
-	GtkHTMLClass parent_class;
+	EWebViewClass parent_class;
 
 	/* Signals */
 	void		(*refresh)		(ESignaturePreview *preview);
diff --git a/widgets/misc/e-web-view.c b/widgets/misc/e-web-view.c
new file mode 100644
index 0000000..a49fd22
--- /dev/null
+++ b/widgets/misc/e-web-view.c
@@ -0,0 +1,1110 @@
+/*
+ * e-web-view.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-web-view.h"
+
+#include <config.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#include <camel/camel-internet-address.h>
+#include <camel/camel-url.h>
+
+#include "e-util/e-util.h"
+#include "e-util/e-plugin-ui.h"
+
+#define E_WEB_VIEW_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_WEB_VIEW, EWebViewPrivate))
+
+typedef struct _EWebViewRequest EWebViewRequest;
+
+struct _EWebViewPrivate {
+	GList *requests;
+	GtkUIManager *ui_manager;
+	gchar *selected_uri;
+};
+
+struct _EWebViewRequest {
+	GFile *file;
+	EWebView *web_view;
+	GCancellable *cancellable;
+	GInputStream *input_stream;
+	GtkHTMLStream *output_stream;
+	gchar buffer[4096];
+};
+
+enum {
+	PROP_0,
+	PROP_ANIMATE,
+	PROP_CARET_MODE,
+	PROP_SELECTED_URI
+};
+
+enum {
+	POPUP_EVENT,
+	STATUS_MESSAGE,
+	STOP_LOADING,
+	UPDATE_ACTIONS,
+	LAST_SIGNAL
+};
+
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
+
+static const gchar *ui =
+"<ui>"
+"  <popup name='context'>"
+"    <placeholder name='custom-actions-1'>"
+"      <menuitem action='http-open'/>"
+"      <menuitem action='send-message'/>"
+"    </placeholder>"
+"    <placeholder name='custom-actions-2'>"
+"      <menuitem action='uri-copy'/>"
+"      <menuitem action='mailto-copy'/>"
+"    </placeholder>"
+"    <placeholder name='custom-actions-3'/>"
+"  </popup>"
+"</ui>";
+
+static EWebViewRequest *
+web_view_request_new (EWebView *web_view,
+                      const gchar *uri,
+                      GtkHTMLStream *stream)
+{
+	EWebViewRequest *request;
+	GList *list;
+
+	request = g_slice_new (EWebViewRequest);
+
+	/* Try to detect file paths posing as URIs. */
+	if (*uri == '/')
+		request->file = g_file_new_for_path (uri);
+	else
+		request->file = g_file_new_for_uri (uri);
+
+	request->web_view = g_object_ref (web_view);
+	request->cancellable = g_cancellable_new ();
+	request->input_stream = NULL;
+	request->output_stream = stream;
+
+	list = request->web_view->priv->requests;
+	list = g_list_prepend (list, request);
+	request->web_view->priv->requests = list;
+
+	return request;
+}
+
+static void
+web_view_request_free (EWebViewRequest *request)
+{
+	GList *list;
+
+	list = request->web_view->priv->requests;
+	list = g_list_remove (list, request);
+	request->web_view->priv->requests = list;
+
+	g_object_unref (request->file);
+	g_object_unref (request->web_view);
+	g_object_unref (request->cancellable);
+
+	if (request->input_stream != NULL)
+		g_object_unref (request->input_stream);
+
+	g_slice_free (EWebViewRequest, request);
+}
+
+static void
+web_view_request_cancel (EWebViewRequest *request)
+{
+	g_cancellable_cancel (request->cancellable);
+}
+
+static gboolean
+web_view_request_check_for_error (EWebViewRequest *request,
+                                  GError *error)
+{
+	GtkHTML *html;
+	GtkHTMLStream *stream;
+
+	if (error == NULL)
+		return FALSE;
+
+	/* XXX Should we log errors that are not cancellations? */
+
+	html = GTK_HTML (request->web_view);
+	stream = request->output_stream;
+
+	gtk_html_end (html, stream, GTK_HTML_STREAM_ERROR);
+	web_view_request_free (request);
+	g_error_free (error);
+
+	return TRUE;
+}
+
+static void
+web_view_request_stream_read_cb (GInputStream *input_stream,
+                                 GAsyncResult *result,
+                                 EWebViewRequest *request)
+{
+	GtkHTML *html;
+	gssize bytes_read;
+	GError *error = NULL;
+
+	html = GTK_HTML (request->web_view);
+	bytes_read = g_input_stream_read_finish (input_stream, result, &error);
+
+	if (web_view_request_check_for_error (request, error))
+		return;
+
+	if (bytes_read == 0) {
+		gtk_html_end (
+			GTK_HTML (request->web_view),
+			request->output_stream, GTK_HTML_STREAM_OK);
+		web_view_request_free (request);
+		return;
+	}
+
+	gtk_html_write (
+		GTK_HTML (request->web_view),
+		request->output_stream, request->buffer, bytes_read);
+
+	g_input_stream_read_async (
+		request->input_stream, request->buffer,
+		sizeof (request->buffer), G_PRIORITY_DEFAULT,
+		request->cancellable, (GAsyncReadyCallback)
+		web_view_request_stream_read_cb, request);
+}
+
+static void
+web_view_request_read_cb (GFile *file,
+                          GAsyncResult *result,
+                          EWebViewRequest *request)
+{
+	GFileInputStream *input_stream;
+	GError *error = NULL;
+
+	/* Input stream might be NULL, so don't use cast macro. */
+	input_stream = g_file_read_finish (file, result, &error);
+	request->input_stream = (GInputStream *) input_stream;
+
+	if (web_view_request_check_for_error (request, error))
+		return;
+
+	g_input_stream_read_async (
+		request->input_stream, request->buffer,
+		sizeof (request->buffer), G_PRIORITY_DEFAULT,
+		request->cancellable, (GAsyncReadyCallback)
+		web_view_request_stream_read_cb, request);
+}
+
+static void
+action_http_open_cb (GtkAction *action,
+                     EWebView *web_view)
+{
+	const gchar *uri;
+	gpointer parent;
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	uri = e_web_view_get_selected_uri (web_view);
+	g_return_if_fail (uri != NULL);
+
+	e_show_uri (parent, uri);
+}
+
+static void
+action_mailto_copy_cb (GtkAction *action,
+                       EWebView *web_view)
+{
+	CamelURL *curl;
+	CamelInternetAddress *inet_addr;
+	GtkClipboard *clipboard;
+	const gchar *uri;
+	gchar *text;
+
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+	uri = e_web_view_get_selected_uri (web_view);
+	g_return_if_fail (uri != NULL);
+
+	/* This should work because we checked it in update_actions(). */
+	curl = camel_url_new (uri, NULL);
+	g_return_if_fail (curl != NULL);
+
+	inet_addr = camel_internet_address_new ();
+	camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
+	text = camel_address_encode (CAMEL_ADDRESS (inet_addr));
+	if (text == NULL || *text == '\0')
+		text = g_strdup (uri + strlen ("mailto:";));
+
+	camel_object_unref (inet_addr);
+	camel_url_free (curl);
+
+	gtk_clipboard_set_text (clipboard, text, -1);
+	gtk_clipboard_store (clipboard);
+
+	g_free (text);
+}
+
+static void
+action_send_message_cb (GtkAction *action,
+                        EWebView *web_view)
+{
+	const gchar *uri;
+	gpointer parent;
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	uri = e_web_view_get_selected_uri (web_view);
+	g_return_if_fail (uri != NULL);
+
+	e_show_uri (parent, uri);
+}
+
+static void
+action_uri_copy_cb (GtkAction *action,
+                    EWebView *web_view)
+{
+	GtkClipboard *clipboard;
+	const gchar *uri;
+
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+	uri = e_web_view_get_selected_uri (web_view);
+	g_return_if_fail (uri != NULL);
+
+	gtk_clipboard_set_text (clipboard, uri, -1);
+	gtk_clipboard_store (clipboard);
+}
+
+static GtkActionEntry uri_entries[] = {
+
+	{ "uri-copy",
+	  GTK_STOCK_COPY,
+	  N_("_Copy Link Location"),
+	  NULL,
+	  N_("Copy the link to the clipboard"),
+	  G_CALLBACK (action_uri_copy_cb) }
+};
+
+static GtkActionEntry http_entries[] = {
+
+	{ "http-open",
+	  "emblem-web",
+	  N_("_Open Link in Browser"),
+	  NULL,
+	  N_("Open the link in a web browser"),
+	  G_CALLBACK (action_http_open_cb) }
+};
+
+static GtkActionEntry mailto_entries[] = {
+
+	{ "mailto-copy",
+	  GTK_STOCK_COPY,
+	  N_("_Copy Email Address"),
+	  NULL,
+	  N_("Copy the email address to the clipboard"),
+	  G_CALLBACK (action_mailto_copy_cb) },
+
+	{ "send-message",
+	  "mail-message-new",
+	  N_("_Send New Message To..."),
+	  NULL,
+	  N_("Send a mail message to this address"),
+	  G_CALLBACK (action_send_message_cb) }
+};
+
+static gboolean
+web_view_button_press_event_cb (EWebView *web_view,
+                                GdkEventButton *event,
+                                GtkHTML *frame)
+{
+	gboolean event_handled = FALSE;
+	gchar *uri;
+
+	if (event != NULL && event->button != 3)
+		return FALSE;
+
+	uri = e_web_view_extract_uri (web_view, event, frame);
+
+	if (uri == NULL || g_str_has_prefix (uri, "##")) {
+		g_free (uri);
+		return FALSE;
+	}
+
+	g_signal_emit (
+		web_view, signals[POPUP_EVENT], 0,
+		event, uri, &event_handled);
+
+	g_free (uri);
+
+	return event_handled;
+}
+
+static void
+web_view_menu_item_select_cb (EWebView *web_view,
+                              GtkWidget *widget)
+{
+	GtkAction *action;
+	const gchar *tooltip;
+
+	action = gtk_widget_get_action (widget);
+	tooltip = gtk_action_get_tooltip (action);
+
+	if (tooltip == NULL)
+		return;
+
+	e_web_view_status_message (web_view, tooltip);
+}
+
+static void
+web_view_menu_item_deselect_cb (EWebView *web_view)
+{
+	e_web_view_status_message (web_view, NULL);
+}
+
+static void
+web_view_connect_proxy_cb (EWebView *web_view,
+                           GtkAction *action,
+                           GtkWidget *proxy)
+{
+	if (!GTK_IS_MENU_ITEM (proxy))
+		return;
+
+	g_signal_connect_swapped (
+		proxy, "select",
+		G_CALLBACK (web_view_menu_item_select_cb), web_view);
+
+	g_signal_connect_swapped (
+		proxy, "deselect",
+		G_CALLBACK (web_view_menu_item_deselect_cb), web_view);
+}
+
+static void
+web_view_set_property (GObject *object,
+                       guint property_id,
+                       const GValue *value,
+                       GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ANIMATE:
+			e_web_view_set_animate (
+				E_WEB_VIEW (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_CARET_MODE:
+			e_web_view_set_caret_mode (
+				E_WEB_VIEW (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_SELECTED_URI:
+			e_web_view_set_selected_uri (
+				E_WEB_VIEW (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+web_view_get_property (GObject *object,
+                       guint property_id,
+                       GValue *value,
+                       GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ANIMATE:
+			g_value_set_boolean (
+				value, e_web_view_get_animate (
+				E_WEB_VIEW (object)));
+			return;
+
+		case PROP_CARET_MODE:
+			g_value_set_boolean (
+				value, e_web_view_get_caret_mode (
+				E_WEB_VIEW (object)));
+			return;
+
+		case PROP_SELECTED_URI:
+			g_value_set_string (
+				value, e_web_view_get_selected_uri (
+				E_WEB_VIEW (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+web_view_dispose (GObject *object)
+{
+	EWebViewPrivate *priv;
+
+	priv = E_WEB_VIEW_GET_PRIVATE (object);
+
+	if (priv->ui_manager != NULL) {
+		g_object_unref (priv->ui_manager);
+		priv->ui_manager = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+web_view_finalize (GObject *object)
+{
+	EWebViewPrivate *priv;
+
+	priv = E_WEB_VIEW_GET_PRIVATE (object);
+
+	/* All URI requests should be complete or cancelled by now. */
+	if (priv->requests != NULL)
+		g_warning ("Finalizing EWebView with active URI requests");
+
+	g_free (priv->selected_uri);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+web_view_button_press_event (GtkWidget *widget,
+                             GdkEventButton *event)
+{
+	GtkWidgetClass *widget_class;
+	EWebView *web_view;
+
+	web_view = E_WEB_VIEW (widget);
+
+	if (web_view_button_press_event_cb (web_view, event, NULL))
+		return TRUE;
+
+	/* Chain up to parent's button_press_event() method. */
+	widget_class = GTK_WIDGET_CLASS (parent_class);
+	return widget_class->button_press_event (widget, event);
+}
+
+static gboolean
+web_view_scroll_event (GtkWidget *widget,
+                       GdkEventScroll *event)
+{
+	if (event->state & GDK_CONTROL_MASK) {
+		switch (event->direction) {
+			case GDK_SCROLL_UP:
+				gtk_html_zoom_in (GTK_HTML (widget));
+				return TRUE;
+			case GDK_SCROLL_DOWN:
+				gtk_html_zoom_out (GTK_HTML (widget));
+				return TRUE;
+			default:
+				break;
+		}
+	}
+
+	return FALSE;
+}
+
+static void
+web_view_url_requested (GtkHTML *html,
+                        const gchar *uri,
+                        GtkHTMLStream *stream)
+{
+	EWebViewRequest *request;
+
+	request = web_view_request_new (E_WEB_VIEW (html), uri, stream);
+
+	g_file_read_async (
+		request->file, G_PRIORITY_DEFAULT,
+		request->cancellable, (GAsyncReadyCallback)
+		web_view_request_read_cb, request);
+}
+
+static void
+web_view_link_clicked (GtkHTML *html,
+                       const gchar *uri)
+{
+	gpointer parent;
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (html));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	e_show_uri (parent, uri);
+}
+
+static void
+web_view_on_url (GtkHTML *html,
+                 const gchar *uri)
+{
+	EWebView *web_view;
+	CamelInternetAddress *address;
+	CamelURL *curl;
+	const gchar *format = NULL;
+	gchar *message = NULL;
+	gchar *who;
+
+	web_view = E_WEB_VIEW (html);
+
+	if (uri == NULL || *uri == '\0')
+		goto exit;
+
+	if (g_str_has_prefix (uri, "mailto:";))
+		format = _("Click to mail %s");
+	else if (g_str_has_prefix (uri, "callto:"))
+		format = _("Click to call %s");
+	else if (g_str_has_prefix (uri, "h323:"))
+		format = _("Click to call %s");
+	else if (g_str_has_prefix (uri, "sip:"))
+		format = _("Click to call %s");
+	else if (g_str_has_prefix (uri, "##"))
+		message = g_strdup (_("Click to hide/unhide addresses"));
+	else
+		message = g_strdup_printf (_("Click to open %s"), uri);
+
+	if (format == NULL)
+		goto exit;
+
+	/* XXX Use something other than Camel here.  Surely
+	 *     there's other APIs around that can do this. */
+	curl = camel_url_new (uri, NULL);
+	address = camel_internet_address_new ();
+	camel_address_decode (CAMEL_ADDRESS (address), curl->path);
+	who = camel_address_format (CAMEL_ADDRESS (address));
+	camel_object_unref (address);
+	camel_url_free (curl);
+
+	if (who == NULL)
+		who = g_strdup (strchr (uri, ':') + 1);
+
+	message = g_strdup_printf (format, who);
+
+	g_free (who);
+
+exit:
+	e_web_view_status_message (web_view, message);
+
+	g_free (message);
+}
+
+static void
+web_view_iframe_created (GtkHTML *html,
+                         GtkHTML *iframe)
+{
+	g_signal_connect_swapped (
+		iframe, "button-press-event",
+		G_CALLBACK (web_view_button_press_event_cb), html);
+}
+
+static gchar *
+web_view_extract_uri (EWebView *web_view,
+                      GdkEventButton *event,
+                      GtkHTML *html)
+{
+	gchar *uri;
+
+	if (event != NULL)
+		uri = gtk_html_get_url_at (html, event->x, event->y);
+	else
+		uri = gtk_html_get_cursor_url (html);
+
+	return uri;
+}
+
+static gboolean
+web_view_popup_event (EWebView *web_view,
+                      GdkEventButton *event,
+                      const gchar *uri)
+{
+	e_web_view_set_selected_uri (web_view, uri);
+	e_web_view_show_popup_menu (web_view, event, NULL, NULL);
+
+	return TRUE;
+}
+
+static void
+web_view_stop_loading (EWebView *web_view)
+{
+	g_list_foreach (
+		web_view->priv->requests, (GFunc)
+		web_view_request_cancel, NULL);
+
+	gtk_html_stop (GTK_HTML (web_view));
+}
+
+static void
+web_view_update_actions (EWebView *web_view)
+{
+	CamelURL *curl;
+	GtkActionGroup *action_group;
+	gboolean scheme_is_http;
+	gboolean scheme_is_mailto;
+	gboolean uri_is_valid;
+	gboolean visible;
+	const gchar *uri;
+
+	uri = e_web_view_get_selected_uri (web_view);
+	g_return_if_fail (uri != NULL);
+
+	/* Parse the URI early so we know if the actions will work. */
+	curl = camel_url_new (uri, NULL);
+	uri_is_valid = (curl != NULL);
+	camel_url_free (curl);
+
+	scheme_is_http =
+		(g_ascii_strncasecmp (uri, "http:", 5) == 0) ||
+		(g_ascii_strncasecmp (uri, "https:", 6) == 0);
+
+	scheme_is_mailto =
+		(g_ascii_strncasecmp (uri, "mailto:";, 7) == 0);
+
+	/* Allow copying the URI even if it's malformed. */
+	visible = !scheme_is_mailto;
+	action_group = e_web_view_get_action_group (web_view, "uri");
+	gtk_action_group_set_visible (action_group, visible);
+
+	visible = uri_is_valid && scheme_is_http;
+	action_group = e_web_view_get_action_group (web_view, "http");
+	gtk_action_group_set_visible (action_group, visible);
+
+	visible = uri_is_valid && scheme_is_mailto;
+	action_group = e_web_view_get_action_group (web_view, "mailto");
+	gtk_action_group_set_visible (action_group, visible);
+}
+
+static void
+web_view_class_init (EWebViewClass *class)
+{
+	GObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+	GtkHTMLClass *html_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EWebViewPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = web_view_set_property;
+	object_class->get_property = web_view_get_property;
+	object_class->dispose = web_view_dispose;
+	object_class->finalize = web_view_finalize;
+
+	widget_class = GTK_WIDGET_CLASS (class);
+	widget_class->button_press_event = web_view_button_press_event;
+	widget_class->scroll_event = web_view_scroll_event;
+
+	html_class = GTK_HTML_CLASS (class);
+	html_class->url_requested = web_view_url_requested;
+	html_class->link_clicked = web_view_link_clicked;
+	html_class->on_url = web_view_on_url;
+	html_class->iframe_created = web_view_iframe_created;
+
+	class->extract_uri = web_view_extract_uri;
+	class->popup_event = web_view_popup_event;
+	class->stop_loading = web_view_stop_loading;
+	class->update_actions = web_view_update_actions;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ANIMATE,
+		g_param_spec_boolean (
+			"animate",
+			"Animate Images",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_CARET_MODE,
+		g_param_spec_boolean (
+			"caret-mode",
+			"Caret Mode",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SELECTED_URI,
+		g_param_spec_string (
+			"selected-uri",
+			"Selected URI",
+			NULL,
+			NULL,
+			G_PARAM_READWRITE));
+
+	signals[POPUP_EVENT] = g_signal_new (
+		"popup-event",
+		G_TYPE_FROM_CLASS (class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EWebViewClass, popup_event),
+		g_signal_accumulator_true_handled, NULL,
+		e_marshal_BOOLEAN__BOXED_STRING,
+		G_TYPE_BOOLEAN, 2,
+		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE,
+		G_TYPE_STRING);
+
+	signals[STATUS_MESSAGE] = g_signal_new (
+		"status-message",
+		G_TYPE_FROM_CLASS (class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EWebViewClass, status_message),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__STRING,
+		G_TYPE_NONE, 1,
+		G_TYPE_STRING);
+
+	signals[STOP_LOADING] = g_signal_new (
+		"stop-loading",
+		G_TYPE_FROM_CLASS (class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EWebViewClass, stop_loading),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	signals[UPDATE_ACTIONS] = g_signal_new (
+		"update-actions",
+		G_TYPE_FROM_CLASS (class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EWebViewClass, update_actions),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+}
+
+static void
+web_view_init (EWebView *web_view)
+{
+	GtkUIManager *ui_manager;
+	GtkActionGroup *action_group;
+	const gchar *domain = GETTEXT_PACKAGE;
+	const gchar *id;
+	GError *error = NULL;
+
+	web_view->priv = E_WEB_VIEW_GET_PRIVATE (web_view);
+
+	ui_manager = gtk_ui_manager_new ();
+	web_view->priv->ui_manager = ui_manager;
+
+	g_signal_connect_swapped (
+		ui_manager, "connect-proxy",
+		G_CALLBACK (web_view_connect_proxy_cb), web_view);
+
+	action_group = gtk_action_group_new ("uri");
+	gtk_action_group_set_translation_domain (action_group, domain);
+	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+	g_object_unref (action_group);
+
+	gtk_action_group_add_actions (
+		action_group, uri_entries,
+		G_N_ELEMENTS (uri_entries), web_view);
+
+	action_group = gtk_action_group_new ("http");
+	gtk_action_group_set_translation_domain (action_group, domain);
+	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+	g_object_unref (action_group);
+
+	gtk_action_group_add_actions (
+		action_group, http_entries,
+		G_N_ELEMENTS (http_entries), web_view);
+
+	action_group = gtk_action_group_new ("mailto");
+	gtk_action_group_set_translation_domain (action_group, domain);
+	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+	g_object_unref (action_group);
+
+	gtk_action_group_add_actions (
+		action_group, mailto_entries,
+		G_N_ELEMENTS (mailto_entries), web_view);
+
+	/* Because we are loading from a hard-coded string, there is
+	 * no chance of I/O errors.  Failure here implies a malformed
+	 * UI definition.  Full stop. */
+	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+	if (error != NULL)
+		g_error ("%s", error->message);
+
+	id = "org.gnome.evolution.webview";
+	e_plugin_ui_register_manager (ui_manager, id, web_view);
+	e_plugin_ui_enable_manager (ui_manager, id);
+}
+
+GType
+e_web_view_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EWebViewClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) web_view_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EWebView),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) web_view_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_HTML, "EWebView", &type_info, 0);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_web_view_new (void)
+{
+	return g_object_new (E_TYPE_WEB_VIEW, NULL);
+}
+
+void
+e_web_view_clear (EWebView *web_view)
+{
+	g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+	gtk_html_load_empty (GTK_HTML (web_view));
+}
+
+void
+e_web_view_load_string (EWebView *web_view,
+                        const gchar *string)
+{
+	g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+	if (string != NULL && *string != '\0')
+		gtk_html_load_from_string (GTK_HTML (web_view), string, -1);
+	else
+		e_web_view_clear (web_view);
+}
+
+gboolean
+e_web_view_get_animate (EWebView *web_view)
+{
+	/* XXX This is just here to maintain symmetry
+	 *     with e_web_view_set_animate(). */
+
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
+
+	return gtk_html_get_animate (GTK_HTML (web_view));
+}
+
+void
+e_web_view_set_animate (EWebView *web_view,
+                        gboolean animate)
+{
+	/* XXX GtkHTML does not utilize GObject properties as well
+	 *     as it could.  This just wraps gtk_html_set_animate()
+	 *     so we can get a "notify::animate" signal. */
+
+	g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+	gtk_html_set_animate (GTK_HTML (web_view), animate);
+
+	g_object_notify (G_OBJECT (web_view), "animate");
+}
+
+gboolean
+e_web_view_get_caret_mode (EWebView *web_view)
+{
+	/* XXX This is just here to maintain symmetry
+	 *     with e_web_view_set_caret_mode(). */
+
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
+
+	return gtk_html_get_caret_mode (GTK_HTML (web_view));
+}
+
+void
+e_web_view_set_caret_mode (EWebView *web_view,
+                           gboolean caret_mode)
+{
+	/* XXX GtkHTML does not utilize GObject properties as well
+	 *     as it could.  This just wraps gtk_html_set_caret_mode()
+	 *     so we can get a "notify::caret-mode" signal. */
+
+	g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+	gtk_html_set_caret_mode (GTK_HTML (web_view), caret_mode);
+
+	g_object_notify (G_OBJECT (web_view), "caret-mode");
+}
+
+const gchar *
+e_web_view_get_selected_uri (EWebView *web_view)
+{
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+
+	return web_view->priv->selected_uri;
+}
+
+void
+e_web_view_set_selected_uri (EWebView *web_view,
+                             const gchar *selected_uri)
+{
+	g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+	g_free (web_view->priv->selected_uri);
+	web_view->priv->selected_uri = g_strdup (selected_uri);
+
+	g_object_notify (G_OBJECT (web_view), "selected-uri");
+}
+
+GtkAction *
+e_web_view_get_action (EWebView *web_view,
+                       const gchar *action_name)
+{
+	GtkUIManager *ui_manager;
+
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+	g_return_val_if_fail (action_name != NULL, NULL);
+
+	ui_manager = e_web_view_get_ui_manager (web_view);
+
+	return e_lookup_action (ui_manager, action_name);
+}
+
+GtkActionGroup *
+e_web_view_get_action_group (EWebView *web_view,
+                             const gchar *group_name)
+{
+	GtkUIManager *ui_manager;
+
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+	g_return_val_if_fail (group_name != NULL, NULL);
+
+	ui_manager = e_web_view_get_ui_manager (web_view);
+
+	return e_lookup_action_group (ui_manager, group_name);
+}
+
+gchar *
+e_web_view_extract_uri (EWebView *web_view,
+                        GdkEventButton *event,
+                        GtkHTML *frame)
+{
+	EWebViewClass *class;
+
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+
+	if (frame == NULL)
+		frame = GTK_HTML (web_view);
+
+	class = E_WEB_VIEW_GET_CLASS (web_view);
+	g_return_val_if_fail (class->extract_uri != NULL, NULL);
+
+	return class->extract_uri (web_view, event, frame);
+}
+
+gboolean
+e_web_view_scroll_forward (EWebView *web_view)
+{
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
+
+	return gtk_html_command (GTK_HTML (web_view), "scroll-forward");
+}
+
+gboolean
+e_web_view_scroll_backward (EWebView *web_view)
+{
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
+
+	return gtk_html_command (GTK_HTML (web_view), "scroll-backward");
+}
+
+GtkUIManager *
+e_web_view_get_ui_manager (EWebView *web_view)
+{
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+
+	return web_view->priv->ui_manager;
+}
+
+GtkWidget *
+e_web_view_get_popup_menu (EWebView *web_view)
+{
+	GtkUIManager *ui_manager;
+	GtkWidget *menu;
+
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+
+	ui_manager = e_web_view_get_ui_manager (web_view);
+	menu = gtk_ui_manager_get_widget (ui_manager, "/context");
+	g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
+
+	return menu;
+}
+
+void
+e_web_view_show_popup_menu (EWebView *web_view,
+                            GdkEventButton *event,
+                            GtkMenuPositionFunc func,
+                            gpointer user_data)
+{
+	GtkWidget *menu;
+
+	g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+	e_web_view_update_actions (web_view);
+
+	menu = e_web_view_get_popup_menu (web_view);
+
+	if (event != NULL)
+		gtk_menu_popup (
+			GTK_MENU (menu), NULL, NULL, func,
+			user_data, event->button, event->time);
+	else
+		gtk_menu_popup (
+			GTK_MENU (menu), NULL, NULL, func,
+			user_data, 0, gtk_get_current_event_time ());
+}
+
+void
+e_web_view_status_message (EWebView *web_view,
+                           const gchar *status_message)
+{
+	g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+	g_signal_emit (web_view, signals[STATUS_MESSAGE], 0, status_message);
+}
+
+void
+e_web_view_stop_loading (EWebView *web_view)
+{
+	g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+	g_signal_emit (web_view, signals[STOP_LOADING], 0);
+}
+
+void
+e_web_view_update_actions (EWebView *web_view)
+{
+	g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+	g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0);
+}
diff --git a/widgets/misc/e-web-view.h b/widgets/misc/e-web-view.h
new file mode 100644
index 0000000..3bce2b4
--- /dev/null
+++ b/widgets/misc/e-web-view.h
@@ -0,0 +1,119 @@
+/*
+ * e-web-view.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/* This is intended to serve as a common base class for all HTML viewing
+ * needs in Evolution.  Currently based on GtkHTML, the idea is to wrap
+ * the GtkHTML API enough that we no longer have to make direct calls to
+ * it.  This should help smooth the transition to WebKit/GTK+.
+ *
+ * This class handles basic tasks like mouse hovers over links, clicked
+ * links, and servicing URI requests asynchronously via GIO. */
+
+#ifndef E_WEB_VIEW_H
+#define E_WEB_VIEW_H
+
+#include <gtkhtml/gtkhtml.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEB_VIEW \
+	(e_web_view_get_type ())
+#define E_WEB_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_WEB_VIEW, EWebView))
+#define E_WEB_VIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_WEB_VIEW, EWebViewClass))
+#define E_IS_WEB_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_WEB_VIEW))
+#define E_IS_WEB_VIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_WEB_VIEW))
+#define E_WEB_VIEW_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_WEB_VIEW, EWebViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EWebView EWebView;
+typedef struct _EWebViewClass EWebViewClass;
+typedef struct _EWebViewPrivate EWebViewPrivate;
+
+struct _EWebView {
+	GtkHTML parent;
+	EWebViewPrivate *priv;
+};
+
+struct _EWebViewClass {
+	GtkHTMLClass parent_class;
+
+	/* Methods */
+	gchar *		(*extract_uri)		(EWebView *web_view,
+						 GdkEventButton *event,
+						 GtkHTML *frame);
+
+	/* Signals */
+	gboolean	(*popup_event)		(EWebView *web_view,
+						 GdkEventButton *event,
+						 const gchar *uri);
+	void		(*status_message)	(EWebView *web_view,
+						 const gchar *status_message);
+	void		(*stop_loading)		(EWebView *web_view);
+	void		(*update_actions)	(EWebView *web_view);
+};
+
+GType		e_web_view_get_type		(void);
+GtkWidget *	e_web_view_new			(void);
+void		e_web_view_clear		(EWebView *web_view);
+void		e_web_view_load_string		(EWebView *web_view,
+						 const gchar *string);
+gboolean	e_web_view_get_animate		(EWebView *web_view);
+void		e_web_view_set_animate		(EWebView *web_view,
+						 gboolean animate);
+gboolean	e_web_view_get_caret_mode	(EWebView *web_view);
+void		e_web_view_set_caret_mode	(EWebView *web_view,
+						 gboolean caret_mode);
+const gchar *	e_web_view_get_selected_uri	(EWebView *web_view);
+void		e_web_view_set_selected_uri	(EWebView *web_view,
+						 const gchar *selected_uri);
+GtkAction *	e_web_view_get_action		(EWebView *web_view,
+						 const gchar *action_name);
+GtkActionGroup *e_web_view_get_action_group	(EWebView *web_view,
+						 const gchar *group_name);
+gchar *		e_web_view_extract_uri		(EWebView *web_view,
+						 GdkEventButton *event,
+						 GtkHTML *frame);
+gboolean	e_web_view_scroll_forward	(EWebView *web_view);
+gboolean	e_web_view_scroll_backward	(EWebView *web_view);
+GtkUIManager *	e_web_view_get_ui_manager	(EWebView *web_view);
+GtkWidget *	e_web_view_get_popup_menu	(EWebView *web_view);
+void		e_web_view_show_popup_menu	(EWebView *web_view,
+						 GdkEventButton *event,
+						 GtkMenuPositionFunc func,
+						 gpointer user_data);
+void		e_web_view_status_message	(EWebView *web_view,
+						 const gchar *status_message);
+void		e_web_view_stop_loading		(EWebView *web_view);
+void		e_web_view_update_actions	(EWebView *web_view);
+
+G_END_DECLS
+
+#endif /* E_WEB_VIEW_H */



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