diff --git a/configure.ac b/configure.ac
index e604a8a..f0ad285 100644
--- a/configure.ac
+++ b/configure.ac
@@ -123,6 +123,33 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE",[Gettext package name])
 AM_GLIB_GNU_GETTEXT
 
 # -----------------------------------------------------------
+# Webkit
+# -----------------------------------------------------------
+AC_ARG_ENABLE(webkit,
+              AS_HELP_STRING([--enable-webkit=@<:@no/yes/auto@:>@],
+                             [build with webkit support]), ,
+                             enable_webkit=auto)
+
+if test "x$enable_webkit" != "xno"; then
+   PKG_CHECK_MODULES(WEBKIT, 
+   [
+      webkit-1.0
+   ], have_webkit="yes", have_webkit="no")
+
+   if test "x$have_webkit" = "xyes"; then
+      AC_DEFINE(HAVE_WEBKIT, 1, [Define if you have libwebkitgtk])
+   fi
+else
+   have_webkit=no
+fi
+
+if test "x$enable_webkit" = "xyes" -a "x$have_webkit" != "xyes"; then
+   AC_MSG_ERROR([Couldn't find webkit dependencies.])
+fi
+
+AM_CONDITIONAL(HAVE_WEBKIT, test "x$have_webkit" = "xyes")
+
+# -----------------------------------------------------------
 # spellchecking checks: enchant and iso-codes
 # -----------------------------------------------------------
 AC_ARG_ENABLE(spell,
diff --git a/data/Makefile.am b/data/Makefile.am
index b4cb549..4058d2f 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -40,12 +40,16 @@ else
 install-data-local:
 endif
 
+htmldir = $(datadir)/empathy
+html_DATA = Template.html
+
 EXTRA_DIST = 				\
 	$(profile_DATA)			\
 	$(desktop_in_files)		\
-	$(desktop_DATA)		\
+	$(desktop_DATA)			\
 	$(schemas_in_files)		\
-	$(schemas_DATA)
+	$(schemas_DATA)			\
+	$(html_DATA)
 
 DISTCLEANFILES = 			\
 	$(desktop_DATA)		\
diff --git a/data/Template.html b/data/Template.html
new file mode 100644
index 0000000..708e85b
--- /dev/null
+++ b/data/Template.html
@@ -0,0 +1,159 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+	<base href="%@">
+	<script type="text/ecmascript" defer="defer">
+	
+		//Appending new content to the message view
+		function appendMessage(html) {
+			shouldScroll = nearBottom();
+		
+			//Remove any existing insertion point
+			insert = document.getElementById("insert");
+			if(insert) insert.parentNode.removeChild(insert);
+
+			//Append the new message to the bottom of our chat block
+			chat = document.getElementById("Chat");
+			range = document.createRange();
+			range.selectNode(chat);
+			documentFragment = range.createContextualFragment(html);
+			chat.appendChild(documentFragment);
+			
+			alignChat(shouldScroll);
+		}
+		function appendMessageNoScroll(html) {
+			//Remove any existing insertion point
+			insert = document.getElementById("insert");
+			if(insert) insert.parentNode.removeChild(insert);
+
+			//Append the new message to the bottom of our chat block
+			chat = document.getElementById("Chat");
+			range = document.createRange();
+			range.selectNode(chat);
+			documentFragment = range.createContextualFragment(html);
+			chat.appendChild(documentFragment);
+		}
+		function appendNextMessage(html){
+			shouldScroll = nearBottom();
+
+			//Locate the insertion point
+			insert = document.getElementById("insert");
+		
+			//make new node
+			range = document.createRange();
+			range.selectNode(insert.parentNode);
+			newNode = range.createContextualFragment(html);
+
+			//swap
+			insert.parentNode.replaceChild(newNode,insert);
+			
+			alignChat(shouldScroll);
+		}
+		function appendNextMessageNoScroll(html){
+			//Locate the insertion point
+			insert = document.getElementById("insert");
+		
+			//make new node
+			range = document.createRange();
+			range.selectNode(insert.parentNode);
+			newNode = range.createContextualFragment(html);
+
+			//swap
+			insert.parentNode.replaceChild(newNode,insert);
+		}
+		
+		//Auto-scroll to bottom.  Use nearBottom to determine if a scrollToBottom is desired.
+		function nearBottom() {
+			return ( document.body.scrollTop >= ( document.body.offsetHeight - ( window.innerHeight * 1.2 ) ) );
+		}
+		function scrollToBottom() {
+			document.body.scrollTop = document.body.offsetHeight;
+		}
+
+		//Dynamically exchange the active stylesheet
+		function setStylesheet( id, url ) {
+			code = "<style id=\"" + id + "\" type=\"text/css\" media=\"screen,print\">";
+			if( url.length ) code += "@import url( \"" + url + "\" );";
+			code += "</style>";
+			range = document.createRange();
+			head = document.getElementsByTagName( "head" ).item(0);
+			range.selectNode( head );
+			documentFragment = range.createContextualFragment( code );
+			head.removeChild( document.getElementById( id ) );
+			head.appendChild( documentFragment );
+		}
+		
+		//Swap an image with its alt-tag text on click, or expand/unexpand an attached image
+		document.onclick = imageCheck;
+		function imageCheck() {		
+			node = event.target;
+			if(node.tagName == 'IMG' && !client.zoomImage(node) && node.alt) {
+				a = document.createElement('a');
+				a.setAttribute('onclick', 'imageSwap(this)');
+				a.setAttribute('src', node.getAttribute('src'));
+				a.className = node.className;
+				text = document.createTextNode(node.alt);
+				a.appendChild(text);
+				node.parentNode.replaceChild(a, node);
+			}
+		}
+
+		function imageSwap(node) {
+			shouldScroll = nearBottom();
+
+			//Swap the image/text
+			img = document.createElement('img');
+			img.setAttribute('src', node.getAttribute('src'));
+			img.setAttribute('alt', node.firstChild.nodeValue);
+			img.className = node.className;
+			node.parentNode.replaceChild(img, node);
+			
+			alignChat(shouldScroll);
+		}
+		
+		//Align our chat to the bottom of the window.  If true is passed, view will also be scrolled down
+		function alignChat(shouldScroll) {
+			var windowHeight = window.innerHeight;
+			
+			if (windowHeight > 0) {
+				var contentElement = document.getElementById('Chat');
+				var contentHeight = contentElement.offsetHeight;
+				if (windowHeight - contentHeight > 0) {
+					contentElement.style.position = 'relative';
+					contentElement.style.top = (windowHeight - contentHeight) + 'px';
+				} else {
+					contentElement.style.position = 'static';
+				}
+			}
+			
+			if (shouldScroll) scrollToBottom();
+		}
+		
+		function windowDidResize(){
+			alignChat(true/*nearBottom()*/); //nearBottom buggy with inactive tabs
+		}
+		
+		window.onresize = windowDidResize;
+	</script>
+	
+	<!-- This style is shared by all variants. !-->
+	<style id="baseStyle" type="text/css" media="screen,print">	
+		%@  
+		*{ word-wrap:break-word; }
+		img.scaledToFitImage { height:auto; width:100%; }
+	</style>
+	
+	<!-- Although we call this mainStyle for legacy reasons, it's actually the variant style !-->
+	<style id="mainStyle" type="text/css" media="screen,print">	
+		@import url( "%@" );
+	</style>
+
+</head>
+<body onload="alignChat(true);" style="==bodyBackground==">
+%@
+<div id="Chat">
+</div>
+%@
+</body>
+</html>
diff --git a/data/empathy.schemas.in b/data/empathy.schemas.in
index a6054cc..02de77d 100644
--- a/data/empathy.schemas.in
+++ b/data/empathy.schemas.in
@@ -188,6 +188,20 @@
     </schema>
 
     <schema>
+      <key>/schemas/apps/empathy/conversation/adium_path</key>
+      <applyto>/apps/empathy/conversation/adium_path</applyto>
+      <owner>empathy</owner>
+      <type>string</type>
+      <default></default>
+      <locale name="C">
+         <short>Path of the adium theme to use</short>
+         <long>
+           Path of the adium theme to use if the theme used for chat is adium.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
       <key>/schemas/apps/empathy/conversation/theme_chat_room</key>
       <applyto>/apps/empathy/conversation/theme_chat_room</applyto>
       <owner>empathy</owner>
diff --git a/docs/libempathy-gtk/libempathy-gtk-docs.sgml b/docs/libempathy-gtk/libempathy-gtk-docs.sgml
index 8c37635..08be8f0 100644
--- a/docs/libempathy-gtk/libempathy-gtk-docs.sgml
+++ b/docs/libempathy-gtk/libempathy-gtk-docs.sgml
@@ -25,6 +25,7 @@
       <xi:include href="xml/empathy-cell-renderer-activatable.xml"/>
       <xi:include href="xml/empathy-cell-renderer-expander.xml"/>
       <xi:include href="xml/empathy-cell-renderer-text.xml"/>
+      <xi:include href="xml/empathy-chat-text-view.xml"/>
       <xi:include href="xml/empathy-chat-view.xml"/>
       <xi:include href="xml/empathy-chat.xml"/>
       <xi:include href="xml/empathy-conf.xml"/>
@@ -43,10 +44,10 @@
       <xi:include href="xml/empathy-smiley-manager.xml"/>
       <xi:include href="xml/empathy-spell-dialog.xml"/>
       <xi:include href="xml/empathy-spell.xml"/>
+      <xi:include href="xml/empathy-theme-adium.xml"/>
       <xi:include href="xml/empathy-theme-boxes.xml"/>
       <xi:include href="xml/empathy-theme-irc.xml"/>
       <xi:include href="xml/empathy-theme-manager.xml"/>
-      <xi:include href="xml/empathy-theme.xml"/>
       <xi:include href="xml/empathy-ui-utils.xml"/>
     </chapter>
   </part>
diff --git a/docs/libempathy-gtk/libempathy-gtk.types b/docs/libempathy-gtk/libempathy-gtk.types
index 59b9ce7..a20054b 100644
--- a/docs/libempathy-gtk/libempathy-gtk.types
+++ b/docs/libempathy-gtk/libempathy-gtk.types
@@ -5,6 +5,7 @@ empathy_cell_renderer_activatable_get_type
 empathy_cell_renderer_expander_get_type
 empathy_cell_renderer_text_get_type
 empathy_chat_get_type
+empathy_chat_text_view_get_type
 empathy_chat_view_get_type
 empathy_conf_get_type
 empathy_contact_list_store_get_type
@@ -12,12 +13,12 @@ empathy_contact_list_view_get_type
 empathy_contact_list_store_sort_get_type
 empathy_contact_list_store_col_get_type
 empathy_contact_list_feature_flags_get_type
-empathy_contact_widget_flags_get_type
 empathy_contact_feature_flags_get_type
+empathy_contact_widget_flags_get_type
 empathy_presence_chooser_get_type
 empathy_smiley_manager_get_type
+empathy_theme_adium_get_type
 empathy_theme_boxes_get_type
-empathy_theme_get_type
 empathy_theme_irc_get_type
 empathy_theme_manager_get_type
 
diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am
index 17cfa4d..8829f48 100644
--- a/libempathy-gtk/Makefile.am
+++ b/libempathy-gtk/Makefile.am
@@ -2,8 +2,10 @@ AM_CPPFLAGS =                                           \
 	-I.                                             \
 	-I$(top_srcdir)                                 \
 	-DDATADIR=\""$(datadir)"\"              	\
+	-DADIUMTHEMESPATH=\""$(datadir)"/empathy/themes\"     	\
 	$(EMPATHY_CFLAGS)				\
 	$(ENCHANT_CFLAGS)				\
+	$(WEBKIT_CFLAGS)				\
 	$(WARN_CFLAGS)
 
 BUILT_SOURCES = 					\
@@ -26,6 +28,7 @@ libempathy_gtk_la_SOURCES =             	\
 	empathy-cell-renderer-expander.c	\
 	empathy-cell-renderer-text.c		\
 	empathy-chat.c				\
+	empathy-chat-text-view.c		\
 	empathy-chat-view.c			\
 	empathy-conf.c				\
 	empathy-contact-dialogs.c		\
@@ -43,7 +46,6 @@ libempathy_gtk_la_SOURCES =             	\
 	empathy-spell.c				\
 	empathy-spell-dialog.c			\
 	empathy-theme-boxes.c			\
-	empathy-theme.c				\
 	empathy-theme-irc.c			\
 	empathy-theme-manager.c			\
 	empathy-ui-utils.c			\
@@ -56,6 +58,7 @@ nodist_libempathy_gtk_la_SOURCES =\
 libempathy_gtk_la_LIBADD =			\
 	$(EMPATHY_LIBS)				\
 	$(ENCHANT_LIBS)				\
+	$(WEBKIT_LIBS)				\
 	$(top_builddir)/libempathy/libempathy.la
 
 libempathy_gtk_la_LDFLAGS =                \
@@ -73,6 +76,7 @@ libempathy_gtk_headers =			\
 	empathy-cell-renderer-expander.h	\
 	empathy-cell-renderer-text.h		\
 	empathy-chat.h				\
+	empathy-chat-text-view.h		\
 	empathy-chat-view.h			\
 	empathy-conf.h				\
 	empathy-contact-dialogs.h		\
@@ -91,7 +95,6 @@ libempathy_gtk_headers =			\
 	empathy-spell.h				\
 	empathy-spell-dialog.h			\
 	empathy-theme-boxes.h			\
-	empathy-theme.h				\
 	empathy-theme-irc.h			\
 	empathy-theme-manager.h			\
 	empathy-ui-utils.h
@@ -101,6 +104,15 @@ libempathy_gtk_include_HEADERS =		\
 	$(libempathy_gtk_headers)		\
 	empathy-gtk-enum-types.h
 
+if HAVE_WEBKIT
+libempathy_gtk_headers += 			\
+	empathy-theme-adium.h			\
+	empathy-theme-adium-manager.h
+libempathy_gtk_la_SOURCES += 			\
+	empathy-theme-adium.c			\
+	empathy-theme-adium-manager.c
+endif
+
 gladedir = $(datadir)/empathy
 glade_DATA = 					\
 	empathy-contact-widget.glade		\
diff --git a/libempathy-gtk/empathy-chat-text-view.c b/libempathy-gtk/empathy-chat-text-view.c
new file mode 100644
index 0000000..389ef88
--- /dev/null
+++ b/libempathy-gtk/empathy-chat-text-view.c
@@ -0,0 +1,1365 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002-2007 Imendio AB
+ * Copyright (C) 2008 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ * Authors: Mikael Hallendal <micke@imendio.com>
+ *          Richard Hult <richard@imendio.com>
+ *          Martyn Russell <martyn@imendio.com>
+ *          Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkmenu.h>
+#include <gtk/gtkmenuitem.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtksizegroup.h>
+#include <glade/glade.h>
+
+#include <telepathy-glib/util.h>
+#include <libmissioncontrol/mc-account.h>
+
+#include <libempathy/empathy-utils.h>
+
+#include "empathy-chat-text-view.h"
+#include "empathy-chat.h"
+#include "empathy-conf.h"
+#include "empathy-ui-utils.h"
+#include "empathy-smiley-manager.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
+#include <libempathy/empathy-debug.h>
+
+/* Number of seconds between timestamps when using normal mode, 5 minutes. */
+#define TIMESTAMP_INTERVAL 300
+
+#define MAX_LINES 800
+#define MAX_SCROLL_TIME 0.4 /* seconds */
+#define SCROLL_DELAY 33     /* milliseconds */
+
+#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatTextView)
+
+typedef struct {
+	GtkTextBuffer        *buffer;
+	guint                 scroll_timeout;
+	GTimer               *scroll_time;
+	GtkTextMark          *find_mark_previous;
+	GtkTextMark          *find_mark_next;
+	gboolean              find_wrapped;
+	gboolean              find_last_direction;
+	EmpathyContact       *last_contact;
+	time_t                last_timestamp;
+	gboolean              allow_scrolling;
+	guint                 notify_system_fonts_id;
+	EmpathySmileyManager *smiley_manager;
+	gboolean              only_if_date;
+} EmpathyChatTextViewPriv;
+
+static void chat_text_view_iface_init (EmpathyChatViewIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EmpathyChatTextView, empathy_chat_text_view,
+			 GTK_TYPE_TEXT_VIEW,
+			 G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CHAT_VIEW,
+						chat_text_view_iface_init));
+
+enum {
+	PROP_0,
+	PROP_LAST_CONTACT,
+	PROP_ONLY_IF_DATE
+};
+
+static gboolean
+chat_text_view_url_event_cb (GtkTextTag    *tag,
+			     GObject       *object,
+			     GdkEvent      *event,
+			     GtkTextIter   *iter,
+			     GtkTextBuffer *buffer)
+{
+	GtkTextIter  start, end;
+	gchar       *str;
+	
+	/* If the link is being selected, don't do anything. */
+	gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+	if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end)) {
+		return FALSE;
+	}
+	
+	if (event->type == GDK_BUTTON_RELEASE && event->button.button == 1) {
+		start = end = *iter;
+		
+		if (gtk_text_iter_backward_to_tag_toggle (&start, tag) &&
+		    gtk_text_iter_forward_to_tag_toggle (&end, tag)) {
+			    str = gtk_text_buffer_get_text (buffer,
+							    &start,
+							    &end,
+							    FALSE);
+			    
+			    empathy_url_show (str);
+			    g_free (str);
+		    }
+	}
+	
+	return FALSE;
+}
+
+static gboolean
+chat_text_view_event_cb (EmpathyChatTextView *view,
+			 GdkEventMotion      *event,
+			 GtkTextTag          *tag)
+{
+	static GdkCursor  *hand = NULL;
+	static GdkCursor  *beam = NULL;
+	GtkTextWindowType  type;
+	GtkTextIter        iter;
+	GdkWindow         *win;
+	gint               x, y, buf_x, buf_y;
+	
+	type = gtk_text_view_get_window_type (GTK_TEXT_VIEW (view),
+					      event->window);
+	
+	if (type != GTK_TEXT_WINDOW_TEXT) {
+		return FALSE;
+	}
+	
+	/* Get where the pointer really is. */
+	win = gtk_text_view_get_window (GTK_TEXT_VIEW (view), type);
+	if (!win) {
+		return FALSE;
+	}
+	
+	gdk_window_get_pointer (win, &x, &y, NULL);
+	
+	/* Get the iter where the cursor is at */
+	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view), type,
+					       x, y,
+					       &buf_x, &buf_y);
+	
+	gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view),
+					    &iter,
+					    buf_x, buf_y);
+	
+	if (gtk_text_iter_has_tag (&iter, tag)) {
+		if (!hand) {
+			hand = gdk_cursor_new (GDK_HAND2);
+			beam = gdk_cursor_new (GDK_XTERM);
+		}
+		gdk_window_set_cursor (win, hand);
+	} else {
+		if (!beam) {
+			beam = gdk_cursor_new (GDK_XTERM);
+		}
+		gdk_window_set_cursor (win, beam);
+	}
+	
+	return FALSE;
+}
+
+static void
+chat_text_view_create_tags (EmpathyChatTextView *view)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (view);
+	GtkTextTag              *tag;
+
+	gtk_text_buffer_create_tag (priv->buffer, EMPATHY_CHAT_TEXT_VIEW_TAG_CUT, NULL);
+	gtk_text_buffer_create_tag (priv->buffer, EMPATHY_CHAT_TEXT_VIEW_TAG_HIGHLIGHT, NULL);
+	gtk_text_buffer_create_tag (priv->buffer, EMPATHY_CHAT_TEXT_VIEW_TAG_SPACING, NULL);
+	gtk_text_buffer_create_tag (priv->buffer, EMPATHY_CHAT_TEXT_VIEW_TAG_TIME, NULL);
+	gtk_text_buffer_create_tag (priv->buffer, EMPATHY_CHAT_TEXT_VIEW_TAG_ACTION, NULL);
+	gtk_text_buffer_create_tag (priv->buffer, EMPATHY_CHAT_TEXT_VIEW_TAG_BODY, NULL);
+	gtk_text_buffer_create_tag (priv->buffer, EMPATHY_CHAT_TEXT_VIEW_TAG_EVENT, NULL);
+
+	tag = gtk_text_buffer_create_tag (priv->buffer, EMPATHY_CHAT_TEXT_VIEW_TAG_LINK, NULL);
+	g_signal_connect (tag, "event",
+			  G_CALLBACK (chat_text_view_url_event_cb),
+			  priv->buffer);
+	
+	g_signal_connect (view, "motion-notify-event",
+			  G_CALLBACK (chat_text_view_event_cb),
+			  tag);
+}
+
+static void
+chat_text_view_system_font_update (EmpathyChatTextView *view)
+{
+	PangoFontDescription *font_description = NULL;
+	gchar                *font_name;
+	
+	if (empathy_conf_get_string (empathy_conf_get (),
+				     "/desktop/gnome/interface/document_font_name",
+				     &font_name) && font_name) {
+					     font_description = pango_font_description_from_string (font_name);
+					     g_free (font_name);
+				     } else {
+					     font_description = NULL;
+				     }
+	
+	gtk_widget_modify_font (GTK_WIDGET (view), font_description);
+	
+	if (font_description) {
+		pango_font_description_free (font_description);
+	}
+}
+
+static void
+chat_text_view_notify_system_font_cb (EmpathyConf *conf,
+				      const gchar *key,
+				      gpointer     user_data)
+{
+	EmpathyChatTextView *view = user_data;
+	
+	chat_text_view_system_font_update (view);
+}
+
+static void
+chat_text_view_open_address_cb (GtkMenuItem *menuitem, const gchar *url)
+{
+	empathy_url_show (url);
+}
+
+static void
+chat_text_view_copy_address_cb (GtkMenuItem *menuitem, const gchar *url)
+{
+	GtkClipboard *clipboard;
+	
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+	gtk_clipboard_set_text (clipboard, url, -1);
+	
+	clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+	gtk_clipboard_set_text (clipboard, url, -1);
+}
+
+static void
+chat_text_view_populate_popup (EmpathyChatTextView *view,
+			       GtkMenu        *menu,
+			       gpointer        user_data)
+{
+	EmpathyChatTextViewPriv *priv;
+	GtkTextTagTable    *table;
+	GtkTextTag         *tag;
+	gint                x, y;
+	GtkTextIter         iter, start, end;
+	GtkWidget          *item;
+	gchar              *str = NULL;
+	
+	priv = GET_PRIV (view);
+	
+	/* Clear menu item */
+	if (gtk_text_buffer_get_char_count (priv->buffer) > 0) {
+		item = gtk_separator_menu_item_new ();
+		gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+		gtk_widget_show (item);
+		
+		item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL);
+		gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+		gtk_widget_show (item);
+		
+		g_signal_connect_swapped (item, "activate",
+					  G_CALLBACK (empathy_chat_view_clear),
+					  view);
+	}
+	
+	/* Link context menu items */
+	table = gtk_text_buffer_get_tag_table (priv->buffer);
+	tag = gtk_text_tag_table_lookup (table, EMPATHY_CHAT_TEXT_VIEW_TAG_LINK);
+	
+	gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y);
+	
+	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
+					       GTK_TEXT_WINDOW_WIDGET,
+					       x, y,
+					       &x, &y);
+	
+	gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &iter, x, y);
+	
+	start = end = iter;
+	
+	if (gtk_text_iter_backward_to_tag_toggle (&start, tag) &&
+	    gtk_text_iter_forward_to_tag_toggle (&end, tag)) {
+		    str = gtk_text_buffer_get_text (priv->buffer,
+						    &start, &end, FALSE);
+	    }
+	
+	if (G_STR_EMPTY (str)) {
+		g_free (str);
+		return;
+	}
+	
+	/* NOTE: Set data just to get the string freed when not needed. */
+	g_object_set_data_full (G_OBJECT (menu),
+				"url", str,
+				(GDestroyNotify) g_free);
+	
+	item = gtk_separator_menu_item_new ();
+	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+	gtk_widget_show (item);
+	
+	item = gtk_menu_item_new_with_mnemonic (_("_Copy Link Address"));
+	g_signal_connect (item, "activate",
+			  G_CALLBACK (chat_text_view_copy_address_cb),
+			  str);
+	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+	gtk_widget_show (item);
+	
+	item = gtk_menu_item_new_with_mnemonic (_("_Open Link"));
+	g_signal_connect (item, "activate",
+			  G_CALLBACK (chat_text_view_open_address_cb),
+			  str);
+	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+	gtk_widget_show (item);
+}
+
+static gboolean
+chat_text_view_is_scrolled_down (EmpathyChatTextView *view)
+{
+	GtkWidget *sw;
+	
+	sw = gtk_widget_get_parent (GTK_WIDGET (view));
+	if (GTK_IS_SCROLLED_WINDOW (sw)) {
+		GtkAdjustment *vadj;
+		
+		vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw));
+		
+		if (vadj->value + vadj->page_size / 2 < vadj->upper - vadj->page_size) {
+			return FALSE;
+		}
+	}
+	
+	return TRUE;
+}
+
+static void
+chat_text_view_maybe_trim_buffer (EmpathyChatTextView *view)
+{
+	EmpathyChatTextViewPriv *priv;
+	GtkTextIter         top, bottom;
+	gint                line;
+	gint                remove;
+	GtkTextTagTable    *table;
+	GtkTextTag         *tag;
+	
+	priv = GET_PRIV (view);
+	
+	gtk_text_buffer_get_end_iter (priv->buffer, &bottom);
+	line = gtk_text_iter_get_line (&bottom);
+	if (line < MAX_LINES) {
+		return;
+	}
+	
+	remove = line - MAX_LINES;
+	gtk_text_buffer_get_start_iter (priv->buffer, &top);
+	
+	bottom = top;
+	if (!gtk_text_iter_forward_lines (&bottom, remove)) {
+		return;
+	}
+	
+	/* Track backwords to a place where we can safely cut, we don't do it in
+	  * the middle of a tag.
+	  */
+	table = gtk_text_buffer_get_tag_table (priv->buffer);
+	tag = gtk_text_tag_table_lookup (table, EMPATHY_CHAT_TEXT_VIEW_TAG_CUT);
+	if (!tag) {
+		return;
+	}
+	
+	if (!gtk_text_iter_forward_to_tag_toggle (&bottom, tag)) {
+		return;
+	}
+	
+	if (!gtk_text_iter_equal (&top, &bottom)) {
+		gtk_text_buffer_delete (priv->buffer, &top, &bottom);
+	}
+}
+
+static void
+chat_text_view_append_timestamp (EmpathyChatTextView *view,
+				 time_t               timestamp,
+				 gboolean             show_date)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (view);
+	GtkTextIter              iter;
+	gchar                   *tmp;
+	GString                 *str;
+
+	str = g_string_new ("- ");
+
+	/* Append date if needed */
+	if (show_date) {
+		GDate *date;
+		gchar  buf[256];
+
+		date = g_date_new ();
+		g_date_set_time_t (date, timestamp);
+		g_date_strftime (buf, 256, _("%A %d %B %Y"), date);
+		g_string_append (str, buf);
+		g_string_append (str, ", ");
+		g_date_free (date);
+	}
+
+	/* Append time */
+	tmp = empathy_time_to_string_local (timestamp, EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
+	g_string_append (str, tmp);
+	g_free (tmp);
+
+	g_string_append (str, " -\n");
+
+	/* Insert the string in the buffer */
+	empathy_chat_text_view_append_spacing (view);
+	gtk_text_buffer_get_end_iter (priv->buffer, &iter);
+	gtk_text_buffer_insert_with_tags_by_name (priv->buffer,
+						  &iter,
+						  str->str, -1,
+						  EMPATHY_CHAT_TEXT_VIEW_TAG_TIME,
+						  NULL);
+
+	priv->last_timestamp = timestamp;
+
+	g_string_free (str, TRUE);	
+}
+
+static void
+chat_text_maybe_append_date_and_time (EmpathyChatTextView *view,
+				      time_t               timestamp)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (view);
+	GDate                   *date, *last_date;
+	gboolean                 append_date = FALSE;
+	gboolean                 append_time = FALSE;
+
+	/* Get the date from last message */
+	last_date = g_date_new ();
+	g_date_set_time_t (last_date, priv->last_timestamp);
+
+	/* Get the date of the message we are appending */
+	date = g_date_new ();
+	g_date_set_time_t (date, timestamp);
+
+	/* If last message was from another day we append date and time */
+	if (g_date_compare (date, last_date) > 0) {
+		append_date = TRUE;
+		append_time = TRUE;
+	}
+	
+	g_date_free (last_date);
+	g_date_free (date);
+
+	/* If last message is 'old' append the time */
+	if (timestamp - priv->last_timestamp >= TIMESTAMP_INTERVAL) {
+		append_time = TRUE;
+	}
+
+	if (append_date || (!priv->only_if_date && append_time)) {
+		chat_text_view_append_timestamp (view, timestamp, append_date);
+	}
+}
+
+static void
+chat_text_view_size_allocate (GtkWidget     *widget,
+			      GtkAllocation *alloc)
+{
+	gboolean down;
+	
+	down = chat_text_view_is_scrolled_down (EMPATHY_CHAT_TEXT_VIEW (widget));
+	
+	GTK_WIDGET_CLASS (empathy_chat_text_view_parent_class)->size_allocate (widget, alloc);
+	
+	if (down) {
+		GtkAdjustment *adj;
+		
+		adj = GTK_TEXT_VIEW (widget)->vadjustment;
+		gtk_adjustment_set_value (adj, adj->upper - adj->page_size);
+	}
+}
+
+static gboolean
+chat_text_view_drag_motion (GtkWidget      *widget,
+			    GdkDragContext *context,
+			    gint            x,
+			    gint            y,
+			    guint           time)
+{
+	/* Don't handle drag motion, since we don't want the view to scroll as
+	 * the result of dragging something across it. */
+	
+	return FALSE;
+}
+
+static void
+chat_text_view_get_property (GObject    *object,
+			     guint       param_id,
+			     GValue     *value,
+			     GParamSpec *pspec)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_LAST_CONTACT:
+		g_value_set_object (value, priv->last_contact);
+		break;
+	case PROP_ONLY_IF_DATE:
+		g_value_set_boolean (value, priv->only_if_date);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+chat_text_view_set_property (GObject      *object,
+			     guint         param_id,
+			     const GValue *value,
+			     GParamSpec   *pspec)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_ONLY_IF_DATE:
+		priv->only_if_date = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+chat_text_view_finalize (GObject *object)
+{
+	EmpathyChatTextView     *view;
+	EmpathyChatTextViewPriv *priv;
+	
+	view = EMPATHY_CHAT_TEXT_VIEW (object);
+	priv = GET_PRIV (view);
+	
+	DEBUG ("%p", object);
+	
+	empathy_conf_notify_remove (empathy_conf_get (), priv->notify_system_fonts_id);
+	
+	if (priv->last_contact) {
+		g_object_unref (priv->last_contact);
+	}
+	if (priv->scroll_time) {
+		g_timer_destroy (priv->scroll_time);
+	}
+	if (priv->scroll_timeout) {
+		g_source_remove (priv->scroll_timeout);
+	}
+	g_object_unref (priv->smiley_manager);
+	
+	G_OBJECT_CLASS (empathy_chat_text_view_parent_class)->finalize (object);
+}
+
+static void
+empathy_chat_text_view_class_init (EmpathyChatTextViewClass *klass)
+{
+	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+	
+	object_class->finalize = chat_text_view_finalize;
+	object_class->get_property = chat_text_view_get_property;
+	object_class->set_property = chat_text_view_set_property;
+
+	widget_class->size_allocate = chat_text_view_size_allocate;
+	widget_class->drag_motion = chat_text_view_drag_motion; 
+
+	g_object_class_install_property (object_class,
+					 PROP_LAST_CONTACT,
+					 g_param_spec_object ("last-contact",
+							      "Last contact",
+							      "The sender of the last received message",
+							      EMPATHY_TYPE_CONTACT,
+							      G_PARAM_READABLE));
+	g_object_class_install_property (object_class,
+					 PROP_ONLY_IF_DATE,
+					 g_param_spec_boolean ("only-if-date",
+							      "Only if date",
+							      "Display timestamp only if the date changes",
+							      FALSE,
+							      G_PARAM_READWRITE));
+
+
+	g_type_class_add_private (object_class, sizeof (EmpathyChatTextViewPriv));
+}
+
+static void
+empathy_chat_text_view_init (EmpathyChatTextView *view)
+{
+	EmpathyChatTextViewPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (view,
+		EMPATHY_TYPE_CHAT_TEXT_VIEW, EmpathyChatTextViewPriv);
+
+	view->priv = priv;	
+	priv->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+	priv->last_timestamp = 0;
+	priv->allow_scrolling = TRUE;
+	priv->smiley_manager = empathy_smiley_manager_new ();
+	
+	g_object_set (view,
+		      "wrap-mode", GTK_WRAP_WORD_CHAR,
+		      "editable", FALSE,
+		      "cursor-visible", FALSE,
+		      NULL);
+	
+	priv->notify_system_fonts_id =
+		empathy_conf_notify_add (empathy_conf_get (),
+					 "/desktop/gnome/interface/document_font_name",
+					 chat_text_view_notify_system_font_cb,
+					 view);
+	chat_text_view_system_font_update (view);
+	chat_text_view_create_tags (view);
+
+	g_signal_connect (view,
+			  "populate-popup",
+			  G_CALLBACK (chat_text_view_populate_popup),
+			  NULL);
+}
+
+/* Code stolen from pidgin/gtkimhtml.c */
+static gboolean
+chat_text_view_scroll_cb (EmpathyChatTextView *view)
+{
+	EmpathyChatTextViewPriv *priv;
+	GtkAdjustment      *adj;
+	gdouble             max_val;
+	
+	priv = GET_PRIV (view);
+	adj = GTK_TEXT_VIEW (view)->vadjustment;
+	max_val = adj->upper - adj->page_size;
+	
+	g_return_val_if_fail (priv->scroll_time != NULL, FALSE);
+	
+	if (g_timer_elapsed (priv->scroll_time, NULL) > MAX_SCROLL_TIME) {
+		/* time's up. jump to the end and kill the timer */
+		gtk_adjustment_set_value (adj, max_val);
+		g_timer_destroy (priv->scroll_time);
+		priv->scroll_time = NULL;
+		priv->scroll_timeout = 0;
+		return FALSE;
+	}
+	
+	/* scroll by 1/3rd the remaining distance */
+	gtk_adjustment_set_value (adj, gtk_adjustment_get_value (adj) + ((max_val - gtk_adjustment_get_value (adj)) / 3));
+	return TRUE;
+}
+
+static void
+chat_text_view_scroll_down (EmpathyChatView *view)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (view);
+	
+	g_return_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view));
+	
+	if (!priv->allow_scrolling) {
+		return;
+	}
+
+	DEBUG ("Scrolling down");
+
+	if (priv->scroll_time) {
+		g_timer_reset (priv->scroll_time);
+	} else {
+		priv->scroll_time = g_timer_new();
+	}
+	if (!priv->scroll_timeout) {
+		priv->scroll_timeout = g_timeout_add (SCROLL_DELAY,
+						      (GSourceFunc) chat_text_view_scroll_cb,
+						      view);
+	}
+}
+
+static void
+chat_text_view_append_message (EmpathyChatView *view,
+			       EmpathyMessage  *msg)
+{
+	EmpathyChatTextView     *text_view = EMPATHY_CHAT_TEXT_VIEW (view);
+	EmpathyChatTextViewPriv *priv = GET_PRIV (text_view);
+	gboolean                 bottom;
+	time_t                   timestamp;
+	
+	g_return_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view));
+	g_return_if_fail (EMPATHY_IS_MESSAGE (msg));
+	
+	if (!empathy_message_get_body (msg)) {
+		return;
+	}
+	
+	bottom = chat_text_view_is_scrolled_down (text_view);
+	
+	chat_text_view_maybe_trim_buffer (EMPATHY_CHAT_TEXT_VIEW (view));
+	
+	timestamp = empathy_message_get_timestamp (msg);
+	chat_text_maybe_append_date_and_time (text_view, timestamp);
+	if (EMPATHY_CHAT_TEXT_VIEW_GET_CLASS (view)->append_message) {
+		EMPATHY_CHAT_TEXT_VIEW_GET_CLASS (view)->append_message (text_view,
+									 msg);
+	}
+	
+	if (bottom) {
+		chat_text_view_scroll_down (view);
+	}
+	
+	if (priv->last_contact) {
+		g_object_unref (priv->last_contact);
+	}
+	priv->last_contact = g_object_ref (empathy_message_get_sender (msg));
+	g_object_notify (G_OBJECT (view), "last-contact");
+}
+
+static void
+chat_text_view_append_event (EmpathyChatView *view,
+			     const gchar     *str)
+{
+	EmpathyChatTextView     *text_view = EMPATHY_CHAT_TEXT_VIEW (view);
+	EmpathyChatTextViewPriv *priv = GET_PRIV (text_view);
+	gboolean                 bottom;
+	GtkTextIter              iter;
+	gchar                   *msg;
+
+
+	g_return_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view));
+	g_return_if_fail (!G_STR_EMPTY (str));
+
+	bottom = chat_text_view_is_scrolled_down (text_view);
+	chat_text_view_maybe_trim_buffer (EMPATHY_CHAT_TEXT_VIEW (view));
+	chat_text_maybe_append_date_and_time (text_view,
+					      empathy_time_get_current ());
+
+	gtk_text_buffer_get_end_iter (priv->buffer, &iter);
+	msg = g_strdup_printf (" - %s\n", str);
+	gtk_text_buffer_insert_with_tags_by_name (priv->buffer, &iter,
+						  msg, -1,
+						  EMPATHY_CHAT_TEXT_VIEW_TAG_EVENT,
+						  NULL);
+	g_free (msg);
+
+	if (bottom) {
+		chat_text_view_scroll_down (view);
+	}
+	
+	if (priv->last_contact) {
+		g_object_unref (priv->last_contact);
+		priv->last_contact = NULL;
+		g_object_notify (G_OBJECT (view), "last-contact");
+	}
+}
+
+static void
+chat_text_view_scroll (EmpathyChatView *view,
+		       gboolean         allow_scrolling)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (view);
+	
+	g_return_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view));
+	
+	DEBUG ("Scrolling %s", allow_scrolling ? "enabled" : "disabled");
+
+	priv->allow_scrolling = allow_scrolling;
+	if (allow_scrolling) {
+		empathy_chat_view_scroll_down (view);
+	}
+}
+
+static gboolean
+chat_text_view_get_has_selection (EmpathyChatView *view)
+{
+	GtkTextBuffer *buffer;
+	
+	g_return_val_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view), FALSE);
+	
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+	
+	return gtk_text_buffer_get_has_selection (buffer);
+}
+
+static void
+chat_text_view_clear (EmpathyChatView *view)
+{
+	GtkTextBuffer      *buffer;
+	EmpathyChatTextViewPriv *priv;
+	
+	g_return_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view));
+	
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+	gtk_text_buffer_set_text (buffer, "", -1);
+	
+	/* We set these back to the initial values so we get
+	  * timestamps when clearing the window to know when
+	  * conversations start.
+	  */
+	priv = GET_PRIV (view);
+	
+	priv->last_timestamp = 0;
+}
+
+static gboolean
+chat_text_view_find_previous (EmpathyChatView *view,
+				const gchar     *search_criteria,
+				gboolean         new_search)
+{
+	EmpathyChatTextViewPriv *priv;
+	GtkTextBuffer      *buffer;
+	GtkTextIter         iter_at_mark;
+	GtkTextIter         iter_match_start;
+	GtkTextIter         iter_match_end;
+	gboolean            found;
+	gboolean            from_start = FALSE;
+	
+	g_return_val_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view), FALSE);
+	g_return_val_if_fail (search_criteria != NULL, FALSE);
+	
+	priv = GET_PRIV (view);
+	
+	buffer = priv->buffer;
+	
+	if (G_STR_EMPTY (search_criteria)) {
+		if (priv->find_mark_previous) {
+			gtk_text_buffer_get_start_iter (buffer, &iter_at_mark);
+			
+			gtk_text_buffer_move_mark (buffer,
+						   priv->find_mark_previous,
+						   &iter_at_mark);
+			gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
+						      priv->find_mark_previous,
+						      0.0,
+						      TRUE,
+						      0.0,
+						      0.0);
+			gtk_text_buffer_select_range (buffer,
+						      &iter_at_mark,
+						      &iter_at_mark);
+		}
+		
+		return FALSE;
+	}
+	
+	if (new_search) {
+		from_start = TRUE;
+	}
+	
+	if (priv->find_mark_previous) {
+		gtk_text_buffer_get_iter_at_mark (buffer,
+						  &iter_at_mark,
+						  priv->find_mark_previous);
+	} else {
+		gtk_text_buffer_get_end_iter (buffer, &iter_at_mark);
+		from_start = TRUE;
+	}
+	
+	priv->find_last_direction = FALSE;
+	
+	found = empathy_text_iter_backward_search (&iter_at_mark,
+						   search_criteria,
+						   &iter_match_start,
+						   &iter_match_end,
+						   NULL);
+	
+	if (!found) {
+		gboolean result = FALSE;
+		
+		if (from_start) {
+			return result;
+		}
+		
+		/* Here we wrap around. */
+		if (!new_search && !priv->find_wrapped) {
+			priv->find_wrapped = TRUE;
+			result = chat_text_view_find_previous (view,
+								 search_criteria, 
+								 FALSE);
+			priv->find_wrapped = FALSE;
+		}
+		
+		return result;
+	}
+	
+	/* Set new mark and show on screen */
+	if (!priv->find_mark_previous) {
+		priv->find_mark_previous = gtk_text_buffer_create_mark (buffer, NULL,
+									&iter_match_start,
+									TRUE);
+	} else {
+		gtk_text_buffer_move_mark (buffer,
+					   priv->find_mark_previous,
+					   &iter_match_start);
+	}
+	
+	if (!priv->find_mark_next) {
+		priv->find_mark_next = gtk_text_buffer_create_mark (buffer, NULL,
+								    &iter_match_end,
+								    TRUE);
+	} else {
+		gtk_text_buffer_move_mark (buffer,
+					   priv->find_mark_next,
+					   &iter_match_end);
+	}
+	
+	gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
+				      priv->find_mark_previous,
+				      0.0,
+				      TRUE,
+				      0.5,
+				      0.5);
+	
+	gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &iter_match_start);
+	gtk_text_buffer_move_mark_by_name (buffer, "insert", &iter_match_end);
+	
+	return TRUE;
+}
+
+static gboolean
+chat_text_view_find_next (EmpathyChatView *view,
+			    const gchar     *search_criteria,
+			    gboolean         new_search)
+{
+	EmpathyChatTextViewPriv *priv;
+	GtkTextBuffer      *buffer;
+	GtkTextIter         iter_at_mark;
+	GtkTextIter         iter_match_start;
+	GtkTextIter         iter_match_end;
+	gboolean            found;
+	gboolean            from_start = FALSE;
+	
+	g_return_val_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view), FALSE);
+	g_return_val_if_fail (search_criteria != NULL, FALSE);
+	
+	priv = GET_PRIV (view);
+	
+	buffer = priv->buffer;
+	
+	if (G_STR_EMPTY (search_criteria)) {
+		if (priv->find_mark_next) {
+			gtk_text_buffer_get_start_iter (buffer, &iter_at_mark);
+			
+			gtk_text_buffer_move_mark (buffer,
+						   priv->find_mark_next,
+						   &iter_at_mark);
+			gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
+						      priv->find_mark_next,
+						      0.0,
+						      TRUE,
+						      0.0,
+						      0.0);
+			gtk_text_buffer_select_range (buffer,
+						      &iter_at_mark,
+						      &iter_at_mark);
+		}
+		
+		return FALSE;
+	}
+	
+	if (new_search) {
+		from_start = TRUE;
+	}
+	
+	if (priv->find_mark_next) {
+		gtk_text_buffer_get_iter_at_mark (buffer,
+						  &iter_at_mark,
+						  priv->find_mark_next);
+	} else {
+		gtk_text_buffer_get_start_iter (buffer, &iter_at_mark);
+		from_start = TRUE;
+	}
+	
+	priv->find_last_direction = TRUE;
+	
+	found = empathy_text_iter_forward_search (&iter_at_mark,
+						  search_criteria,
+						  &iter_match_start,
+						  &iter_match_end,
+						  NULL);
+	
+	if (!found) {
+		gboolean result = FALSE;
+		
+		if (from_start) {
+			return result;
+		}
+		
+		/* Here we wrap around. */
+		if (!new_search && !priv->find_wrapped) {
+			priv->find_wrapped = TRUE;
+			result = chat_text_view_find_next (view, 
+							     search_criteria, 
+							     FALSE);
+			priv->find_wrapped = FALSE;
+		}
+		
+		return result;
+	}
+	
+	/* Set new mark and show on screen */
+	if (!priv->find_mark_next) {
+		priv->find_mark_next = gtk_text_buffer_create_mark (buffer, NULL,
+								    &iter_match_end,
+								    TRUE);
+	} else {
+		gtk_text_buffer_move_mark (buffer,
+					   priv->find_mark_next,
+					   &iter_match_end);
+	}
+	
+	if (!priv->find_mark_previous) {
+		priv->find_mark_previous = gtk_text_buffer_create_mark (buffer, NULL,
+									&iter_match_start,
+									TRUE);
+	} else {
+		gtk_text_buffer_move_mark (buffer,
+					   priv->find_mark_previous,
+					   &iter_match_start);
+	}
+	
+	gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
+				      priv->find_mark_next,
+				      0.0,
+				      TRUE,
+				      0.5,
+				      0.5);
+	
+	gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &iter_match_start);
+	gtk_text_buffer_move_mark_by_name (buffer, "insert", &iter_match_end);
+	
+	return TRUE;
+}
+
+static void
+chat_text_view_find_abilities (EmpathyChatView *view,
+				 const gchar    *search_criteria,
+				 gboolean       *can_do_previous,
+				 gboolean       *can_do_next)
+{
+	EmpathyChatTextViewPriv *priv;
+	GtkTextBuffer           *buffer;
+	GtkTextIter              iter_at_mark;
+	GtkTextIter              iter_match_start;
+	GtkTextIter              iter_match_end;
+	
+	g_return_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view));
+	g_return_if_fail (search_criteria != NULL);
+	g_return_if_fail (can_do_previous != NULL && can_do_next != NULL);
+	
+	priv = GET_PRIV (view);
+	
+	buffer = priv->buffer;
+	
+	if (can_do_previous) {
+		if (priv->find_mark_previous) {
+			gtk_text_buffer_get_iter_at_mark (buffer,
+							  &iter_at_mark,
+							  priv->find_mark_previous);
+		} else {
+			gtk_text_buffer_get_start_iter (buffer, &iter_at_mark);
+		}
+		
+		*can_do_previous = empathy_text_iter_backward_search (&iter_at_mark,
+								      search_criteria,
+								      &iter_match_start,
+								      &iter_match_end,
+								      NULL);
+	}
+	
+	if (can_do_next) {
+		if (priv->find_mark_next) {
+			gtk_text_buffer_get_iter_at_mark (buffer,
+							  &iter_at_mark,
+							  priv->find_mark_next);
+		} else {
+			gtk_text_buffer_get_start_iter (buffer, &iter_at_mark);
+		}
+		
+		*can_do_next = empathy_text_iter_forward_search (&iter_at_mark,
+								 search_criteria,
+								 &iter_match_start,
+								 &iter_match_end,
+								 NULL);
+	}
+}
+
+static void
+chat_text_view_highlight (EmpathyChatView *view,
+			    const gchar     *text)
+{
+	GtkTextBuffer *buffer;
+	GtkTextIter    iter;
+	GtkTextIter    iter_start;
+	GtkTextIter    iter_end;
+	GtkTextIter    iter_match_start;
+	GtkTextIter    iter_match_end;
+	gboolean       found;
+	
+	g_return_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view));
+	
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+	
+	gtk_text_buffer_get_start_iter (buffer, &iter);
+	
+	gtk_text_buffer_get_bounds (buffer, &iter_start, &iter_end);
+	gtk_text_buffer_remove_tag_by_name (buffer, EMPATHY_CHAT_TEXT_VIEW_TAG_HIGHLIGHT,
+					    &iter_start,
+					    &iter_end);
+	
+	if (G_STR_EMPTY (text)) {
+		return;
+	}
+	
+	while (1) {
+		found = empathy_text_iter_forward_search (&iter,
+							  text,
+							  &iter_match_start,
+							  &iter_match_end,
+							  NULL);
+		
+		if (!found) {
+			break;
+		}
+		
+		gtk_text_buffer_apply_tag_by_name (buffer, EMPATHY_CHAT_TEXT_VIEW_TAG_HIGHLIGHT,
+						   &iter_match_start,
+						   &iter_match_end);
+		
+		iter = iter_match_end;
+		gtk_text_iter_forward_char (&iter);
+	}
+}
+
+static void
+chat_text_view_copy_clipboard (EmpathyChatView *view)
+{
+	GtkTextBuffer *buffer;
+	GtkClipboard  *clipboard;
+	
+	g_return_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view));
+	
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+	
+	gtk_text_buffer_copy_clipboard (buffer, clipboard);
+}
+
+static void
+chat_text_view_iface_init (EmpathyChatViewIface *iface)
+{
+	iface->append_message = chat_text_view_append_message;
+	iface->append_event = chat_text_view_append_event;
+	iface->scroll = chat_text_view_scroll;
+	iface->scroll_down = chat_text_view_scroll_down;
+	iface->get_has_selection = chat_text_view_get_has_selection;
+	iface->clear = chat_text_view_clear;
+	iface->find_previous = chat_text_view_find_previous;
+	iface->find_next = chat_text_view_find_next;
+	iface->find_abilities = chat_text_view_find_abilities;
+	iface->highlight = chat_text_view_highlight;
+	iface->copy_clipboard = chat_text_view_copy_clipboard;
+}
+
+EmpathyContact *
+empathy_chat_text_view_get_last_contact (EmpathyChatTextView *view)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (view);
+	
+	g_return_val_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view), NULL);
+	
+	return priv->last_contact;
+}
+
+void
+empathy_chat_text_view_set_only_if_date (EmpathyChatTextView *view,
+					 gboolean             only_if_date)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (view);
+	
+	g_return_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view));
+
+	if (only_if_date != priv->only_if_date) {
+		priv->only_if_date = only_if_date;
+		g_object_notify (G_OBJECT (view), "only-if-date");
+	}
+}
+
+static void
+chat_text_view_insert_text_with_emoticons (EmpathyChatTextView *view,
+					   GtkTextIter         *iter,
+					   const gchar         *str)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (view);
+	gboolean                 use_smileys = FALSE;
+	GSList                  *smileys, *l;
+
+	empathy_conf_get_bool (empathy_conf_get (),
+			       EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
+			       &use_smileys);
+
+	if (!use_smileys) {
+		gtk_text_buffer_insert (priv->buffer, iter, str, -1);
+		return;
+	}
+
+	smileys = empathy_smiley_manager_parse (priv->smiley_manager, str);
+	for (l = smileys; l; l = l->next) {
+		EmpathySmiley *smiley;
+
+		smiley = l->data;
+		if (smiley->pixbuf) {
+			gtk_text_buffer_insert_pixbuf (priv->buffer, iter, smiley->pixbuf);
+		} else {
+			gtk_text_buffer_insert (priv->buffer, iter, smiley->str, -1);
+		}
+		empathy_smiley_free (smiley);
+	}
+	g_slist_free (smileys);
+}
+
+void
+empathy_chat_text_view_append_body (EmpathyChatTextView *view,
+				    const gchar         *body,
+				    const gchar         *tag)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (view);
+	GtkTextIter              start_iter, end_iter;
+	GtkTextMark             *mark;
+	GtkTextIter              iter;
+	gint                     num_matches, i;
+	GArray                  *start, *end;
+
+	g_return_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view));
+
+	gtk_text_buffer_get_end_iter (priv->buffer, &start_iter);
+	mark = gtk_text_buffer_create_mark (priv->buffer, NULL, &start_iter, TRUE);
+
+	start = g_array_new (FALSE, FALSE, sizeof (gint));
+	end = g_array_new (FALSE, FALSE, sizeof (gint));
+
+	num_matches = empathy_regex_match (EMPATHY_REGEX_ALL, body, start, end);
+
+	if (num_matches == 0) {
+		gtk_text_buffer_get_end_iter (priv->buffer, &iter);
+		chat_text_view_insert_text_with_emoticons (view, &iter, body);
+	} else {
+		gint   last = 0;
+		gint   s = 0, e = 0;
+		gchar *tmp;
+
+		for (i = 0; i < num_matches; i++) {
+			s = g_array_index (start, gint, i);
+			e = g_array_index (end, gint, i);
+
+			if (s > last) {
+				tmp = empathy_substring (body, last, s);
+
+				gtk_text_buffer_get_end_iter (priv->buffer, &iter);
+				chat_text_view_insert_text_with_emoticons (view,
+									   &iter,
+									   tmp);
+				g_free (tmp);
+			}
+
+			tmp = empathy_substring (body, s, e);
+
+			gtk_text_buffer_get_end_iter (priv->buffer, &iter);
+			gtk_text_buffer_insert_with_tags_by_name (priv->buffer,
+								  &iter,
+								  tmp,
+								  -1,
+								  EMPATHY_CHAT_TEXT_VIEW_TAG_LINK,
+								  NULL);
+			g_free (tmp);
+
+			last = e;
+		}
+
+		if (e < strlen (body)) {
+			tmp = empathy_substring (body, e, strlen (body));
+
+			gtk_text_buffer_get_end_iter (priv->buffer, &iter);
+			chat_text_view_insert_text_with_emoticons (view,
+								   &iter,
+								   tmp);
+			g_free (tmp);
+		}
+	}
+
+	g_array_free (start, TRUE);
+	g_array_free (end, TRUE);
+
+	gtk_text_buffer_get_end_iter (priv->buffer, &iter);
+	gtk_text_buffer_insert (priv->buffer, &iter, "\n", 1);
+
+	/* Apply the style to the inserted text. */
+	gtk_text_buffer_get_iter_at_mark (priv->buffer, &start_iter, mark);
+	gtk_text_buffer_get_end_iter (priv->buffer, &end_iter);
+
+	gtk_text_buffer_apply_tag_by_name (priv->buffer,
+					   tag,
+					   &start_iter,
+					   &end_iter);
+
+	gtk_text_buffer_delete_mark (priv->buffer, mark);
+}
+
+void
+empathy_chat_text_view_append_spacing (EmpathyChatTextView *view)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (view);
+	GtkTextIter              iter;
+
+	gtk_text_buffer_get_end_iter (priv->buffer, &iter);
+	gtk_text_buffer_insert_with_tags_by_name (priv->buffer,
+						  &iter,
+						  "\n",
+						  -1,
+						  EMPATHY_CHAT_TEXT_VIEW_TAG_CUT,
+						  EMPATHY_CHAT_TEXT_VIEW_TAG_SPACING,
+						  NULL);
+}
+
+GtkTextTag *
+empathy_chat_text_view_tag_set (EmpathyChatTextView *view,
+				const gchar         *tag_name,
+				const gchar         *first_property_name,
+				...)
+{
+	EmpathyChatTextViewPriv *priv = GET_PRIV (view);
+	GtkTextTag              *tag;
+	GtkTextTagTable         *table;
+	va_list                  list;
+
+	g_return_val_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view), NULL);
+	g_return_val_if_fail (tag_name != NULL, NULL);
+
+	table = gtk_text_buffer_get_tag_table (priv->buffer);
+	tag = gtk_text_tag_table_lookup (table, tag_name);
+	g_return_val_if_fail (tag != NULL, NULL);
+
+	if (first_property_name) {
+		va_start (list, first_property_name);
+		g_object_set_valist (G_OBJECT (tag), first_property_name, list);
+		va_end (list);
+	}
+
+	return tag;
+}
+
diff --git a/libempathy-gtk/empathy-chat-text-view.h b/libempathy-gtk/empathy-chat-text-view.h
new file mode 100644
index 0000000..120d370
--- /dev/null
+++ b/libempathy-gtk/empathy-chat-text-view.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002-2007 Imendio AB
+ * Copyright (C) 2008 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ * Authors: Mikael Hallendal <micke@imendio.com>
+ *          Richard Hult <richard@imendio.com>
+ *          Martyn Russell <martyn@imendio.com>
+ *          Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#ifndef __EMPATHY_CHAT_TEXT_VIEW_H__
+#define __EMPATHY_CHAT_TEXT_VIEW_H__
+
+#include <gtk/gtktextview.h>
+
+#include <libempathy/empathy-contact.h>
+#include <libempathy/empathy-message.h>
+
+#include "empathy-chat-view.h"
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_CHAT_TEXT_VIEW         (empathy_chat_text_view_get_type ())
+#define EMPATHY_CHAT_TEXT_VIEW(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CHAT_TEXT_VIEW, EmpathyChatTextView))
+#define EMPATHY_CHAT_TEXT_VIEW_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CHAT_TEXT_VIEW, EmpathyChatTextViewClass))
+#define EMPATHY_IS_CHAT_TEXT_VIEW(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CHAT_TEXT_VIEW))
+#define EMPATHY_IS_CHAT_TEXT_VIEW_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CHAT_TEXT_VIEW))
+#define EMPATHY_CHAT_TEXT_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CHAT_TEXT_VIEW, EmpathyChatTextViewClass))
+
+typedef struct _EmpathyChatTextView      EmpathyChatTextView;
+typedef struct _EmpathyChatTextViewClass EmpathyChatTextViewClass;
+
+struct _EmpathyChatTextView {
+	GtkTextView parent;
+	gpointer priv;
+};
+
+struct _EmpathyChatTextViewClass {
+	GtkTextViewClass parent_class;
+
+	/* <vtable> */
+	void (*append_message) (EmpathyChatTextView *view,
+				EmpathyMessage      *message);
+};
+
+#define EMPATHY_CHAT_TEXT_VIEW_TAG_CUT "cut"
+#define EMPATHY_CHAT_TEXT_VIEW_TAG_HIGHLIGHT "highlight"
+#define EMPATHY_CHAT_TEXT_VIEW_TAG_SPACING "spacing"
+#define EMPATHY_CHAT_TEXT_VIEW_TAG_TIME "time"
+#define EMPATHY_CHAT_TEXT_VIEW_TAG_ACTION "action"
+#define EMPATHY_CHAT_TEXT_VIEW_TAG_BODY "body"
+#define EMPATHY_CHAT_TEXT_VIEW_TAG_EVENT "event"
+#define EMPATHY_CHAT_TEXT_VIEW_TAG_LINK "link"
+
+GType                empathy_chat_text_view_get_type         (void) G_GNUC_CONST;
+EmpathyContact *     empathy_chat_text_view_get_last_contact (EmpathyChatTextView *view);
+void                 empathy_chat_text_view_set_only_if_date (EmpathyChatTextView *view,
+							      gboolean             only_if_date);
+void                 empathy_chat_text_view_append_body      (EmpathyChatTextView *view,
+							      const gchar         *body,
+							      const gchar         *tag);
+void                 empathy_chat_text_view_append_spacing   (EmpathyChatTextView *view);
+GtkTextTag *         empathy_chat_text_view_tag_set          (EmpathyChatTextView *view,
+							      const gchar         *tag_name,
+							      const gchar         *first_property_name,
+							      ...);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_CHAT_TEXT_VIEW_H__ */
diff --git a/libempathy-gtk/empathy-chat-view.c b/libempathy-gtk/empathy-chat-view.c
index 2131cec..23877a2 100644
--- a/libempathy-gtk/empathy-chat-view.c
+++ b/libempathy-gtk/empathy-chat-view.c
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * Copyright (C) 2002-2007 Imendio AB
+ * Copyright (C) 2008 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -17,777 +17,45 @@
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  * 
- * Authors: Mikael Hallendal <micke@imendio.com>
- *          Richard Hult <richard@imendio.com>
- *          Martyn Russell <martyn@imendio.com>
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
  */
 
 #include "config.h"
 
-#include <sys/types.h>
-#include <string.h>
-#include <time.h>
-
-#include <glib/gi18n.h>
-#include <gtk/gtkbutton.h>
-#include <gtk/gtkimage.h>
-#include <gtk/gtkmenu.h>
-#include <gtk/gtkmenuitem.h>
-#include <gtk/gtkimagemenuitem.h>
-#include <gtk/gtkstock.h>
-#include <gtk/gtkscrolledwindow.h>
-#include <gtk/gtksizegroup.h>
-#include <glade/glade.h>
-
-#include <telepathy-glib/util.h>
-#include <libmissioncontrol/mc-account.h>
-
-#include <libempathy/empathy-utils.h>
-
 #include "empathy-chat-view.h"
-#include "empathy-chat.h"
-#include "empathy-conf.h"
-#include "empathy-theme-manager.h"
-#include "empathy-ui-utils.h"
 #include "empathy-smiley-manager.h"
 
-#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
-#include <libempathy/empathy-debug.h>
-
-/* Number of seconds between timestamps when using normal mode, 5 minutes. */
-#define TIMESTAMP_INTERVAL 300
-
-#define MAX_LINES 800
-#define MAX_SCROLL_TIME 0.4 /* seconds */
-#define SCROLL_DELAY 33     /* milliseconds */
-
-#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatView)
-typedef struct {
-	GtkTextBuffer *buffer;
-
-	EmpathyTheme   *theme;
-
-	time_t         last_timestamp;
-
-	gboolean       allow_scrolling;
-	guint          scroll_timeout;
-	GTimer        *scroll_time;
-
-	GtkTextMark   *find_mark_previous;
-	GtkTextMark   *find_mark_next;
-	gboolean       find_wrapped;
-	gboolean       find_last_direction;
-
-	/* This is for the group chat so we know if the "other" last contact
-	 * changed, so we know whether to insert a header or not.
-	 */
-	EmpathyContact *last_contact;
-
-	guint          notify_system_fonts_id;
-	guint          notify_show_avatars_id;
-} EmpathyChatViewPriv;
-
-static void     chat_view_finalize                   (GObject                  *object);
-static gboolean chat_view_drag_motion                (GtkWidget                *widget,
-						      GdkDragContext           *context,
-						      gint                      x,
-						      gint                      y,
-						      guint                     time);
-static void     chat_view_size_allocate              (GtkWidget                *widget,
-						      GtkAllocation            *alloc);
-static void     chat_view_setup_tags                 (EmpathyChatView           *view);
-static void     chat_view_system_font_update         (EmpathyChatView           *view);
-static void     chat_view_notify_system_font_cb      (EmpathyConf               *conf,
-						      const gchar              *key,
-						      gpointer                  user_data);
-static void     chat_view_notify_show_avatars_cb     (EmpathyConf               *conf,
-						      const gchar              *key,
-						      gpointer                  user_data);
-static void     chat_view_populate_popup             (EmpathyChatView           *view,
-						      GtkMenu                  *menu,
-						      gpointer                  user_data);
-static gboolean chat_view_event_cb                   (EmpathyChatView           *view,
-						      GdkEventMotion           *event,
-						      GtkTextTag               *tag);
-static gboolean chat_view_url_event_cb               (GtkTextTag               *tag,
-						      GObject                  *object,
-						      GdkEvent                 *event,
-						      GtkTextIter              *iter,
-						      GtkTextBuffer            *buffer);
-static void     chat_view_open_address_cb            (GtkMenuItem              *menuitem,
-						      const gchar              *url);
-static void     chat_view_copy_address_cb            (GtkMenuItem              *menuitem,
-						      const gchar              *url);
-static void     chat_view_clear_view_cb              (GtkMenuItem              *menuitem,
-						      EmpathyChatView           *view);
-static gboolean chat_view_is_scrolled_down           (EmpathyChatView           *view);
-static void     chat_view_theme_changed_cb           (EmpathyThemeManager       *manager,
-						      EmpathyChatView           *view);
-static void     chat_view_theme_notify_cb            (EmpathyTheme              *theme,
-						      GParamSpec                *param,
-						      EmpathyChatView           *view);
-
-G_DEFINE_TYPE (EmpathyChatView, empathy_chat_view, GTK_TYPE_TEXT_VIEW);
-
-static void
-empathy_chat_view_class_init (EmpathyChatViewClass *klass)
-{
-	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
-	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-
-	object_class->finalize = chat_view_finalize;
-	widget_class->size_allocate = chat_view_size_allocate;
- 	widget_class->drag_motion = chat_view_drag_motion; 
-
-	g_type_class_add_private (object_class, sizeof (EmpathyChatViewPriv));
-}
-
-static void
-empathy_chat_view_init (EmpathyChatView *view)
-{
-	gboolean             show_avatars;
-	EmpathyChatViewPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (view,
-		EMPATHY_TYPE_CHAT_VIEW, EmpathyChatViewPriv);
-
-	view->priv = priv;
-	priv->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-	priv->last_timestamp = 0;
-	priv->allow_scrolling = TRUE;
-
-	g_object_set (view,
-		      "wrap-mode", GTK_WRAP_WORD_CHAR,
-		      "editable", FALSE,
-		      "cursor-visible", FALSE,
-		      NULL);
-
-	priv->notify_system_fonts_id =
-		empathy_conf_notify_add (empathy_conf_get (),
-					 "/desktop/gnome/interface/document_font_name",
-					 chat_view_notify_system_font_cb,
-					 view);
-	chat_view_system_font_update (view);
-
-	priv->notify_show_avatars_id =
-		empathy_conf_notify_add (empathy_conf_get (),
-					 EMPATHY_PREFS_UI_SHOW_AVATARS,
-					 chat_view_notify_show_avatars_cb,
-					 view);
-
-	chat_view_setup_tags (view);
-
-	empathy_theme_manager_apply_saved (empathy_theme_manager_get (), view);
-
-	show_avatars = FALSE;
-	empathy_conf_get_bool (empathy_conf_get (),
-			       EMPATHY_PREFS_UI_SHOW_AVATARS,
-			       &show_avatars);
-
-	empathy_theme_set_show_avatars (priv->theme, show_avatars);
-
-	g_signal_connect (view,
-			  "populate-popup",
-			  G_CALLBACK (chat_view_populate_popup),
-			  NULL);
-
-	g_signal_connect_object (empathy_theme_manager_get (),
-				 "theme-changed",
-				 G_CALLBACK (chat_view_theme_changed_cb),
-				 view,
-				 0);
-}
-
-static void
-chat_view_finalize (GObject *object)
-{
-	EmpathyChatView     *view;
-	EmpathyChatViewPriv *priv;
-
-	view = EMPATHY_CHAT_VIEW (object);
-	priv = GET_PRIV (view);
-
-	DEBUG ("finalize: %p", object);
-
-	empathy_conf_notify_remove (empathy_conf_get (), priv->notify_system_fonts_id);
-	empathy_conf_notify_remove (empathy_conf_get (), priv->notify_show_avatars_id);
-
-	if (priv->last_contact) {
-		g_object_unref (priv->last_contact);
-	}
-	if (priv->scroll_time) {
-		g_timer_destroy (priv->scroll_time);
-	}
-	if (priv->scroll_timeout) {
-		g_source_remove (priv->scroll_timeout);
-	}
-
-	if (priv->theme) {
-		g_signal_handlers_disconnect_by_func (priv->theme,
-						      chat_view_theme_notify_cb,
-						      view);
-		g_object_unref (priv->theme);
-	}
-
-	G_OBJECT_CLASS (empathy_chat_view_parent_class)->finalize (object);
-}
-
-static gboolean
-chat_view_drag_motion (GtkWidget        *widget,
-		       GdkDragContext   *context,
-		       gint              x,
-		       gint              y,
-		       guint             time)
-{
-	/* Don't handle drag motion, since we don't want the view to scroll as
-	 * the result of dragging something across it.
-	 */
-
-	return FALSE;
-}
-
-static void
-chat_view_size_allocate (GtkWidget     *widget,
-			 GtkAllocation *alloc)
-{
-	gboolean down;
-
-	down = chat_view_is_scrolled_down (EMPATHY_CHAT_VIEW (widget));
-
-	GTK_WIDGET_CLASS (empathy_chat_view_parent_class)->size_allocate (widget, alloc);
-
-	if (down) {
-		GtkAdjustment *adj;
-
-		adj = GTK_TEXT_VIEW (widget)->vadjustment;
-		gtk_adjustment_set_value (adj, adj->upper - adj->page_size);
-	}
-}
-
-static void
-chat_view_setup_tags (EmpathyChatView *view)
-{
-	EmpathyChatViewPriv *priv;
-	GtkTextTag         *tag;
-
-	priv = GET_PRIV (view);
-
-	gtk_text_buffer_create_tag (priv->buffer,
-				    "cut",
-				    NULL);
-
-	/* FIXME: Move to the theme and come up with something that looks a bit
-	 * nicer.
-	 */
-	gtk_text_buffer_create_tag (priv->buffer,
-				    "highlight",
-				    "background", "yellow",
-				    NULL);
-
-	tag = gtk_text_buffer_create_tag (priv->buffer,
-					  "link",
-					  NULL);
-
-	g_signal_connect (tag,
-			  "event",
-			  G_CALLBACK (chat_view_url_event_cb),
-			  priv->buffer);
-
-	g_signal_connect (view,
-			  "motion-notify-event",
-			  G_CALLBACK (chat_view_event_cb),
-			  tag);
-}
-
-static void
-chat_view_system_font_update (EmpathyChatView *view)
-{
-	PangoFontDescription *font_description = NULL;
-	gchar                *font_name;
-
-	if (empathy_conf_get_string (empathy_conf_get (),
-				     "/desktop/gnome/interface/document_font_name",
-				     &font_name) && font_name) {
-		font_description = pango_font_description_from_string (font_name);
-		g_free (font_name);
-	} else {
-		font_description = NULL;
-	}
-
-	gtk_widget_modify_font (GTK_WIDGET (view), font_description);
-
-	if (font_description) {
-		pango_font_description_free (font_description);
-	}
-}
-
-static void
-chat_view_notify_system_font_cb (EmpathyConf  *conf,
-				 const gchar *key,
-				 gpointer     user_data)
-{
-	EmpathyChatView *view;
-	EmpathyChatViewPriv *priv;
-	gboolean        show_avatars = FALSE;
-
-	view = user_data;
-	priv = GET_PRIV (view);
-
-	chat_view_system_font_update (view);
-
-	/* Ugly, again, to adjust the vertical position of the nick... Will fix
-	 * this when reworking the theme manager so that view register
-	 * themselves with it instead of the other way around.
-	 */
-	empathy_conf_get_bool (conf,
-			       EMPATHY_PREFS_UI_SHOW_AVATARS,
-			       &show_avatars);
-
-	empathy_theme_set_show_avatars (priv->theme, show_avatars);
-}
-
-static void
-chat_view_notify_show_avatars_cb (EmpathyConf  *conf,
-				  const gchar *key,
-				  gpointer     user_data)
-{
-	EmpathyChatView     *view;
-	EmpathyChatViewPriv *priv;
-	gboolean            show_avatars = FALSE;
-
-	view = user_data;
-	priv = GET_PRIV (view);
-
-	empathy_conf_get_bool (conf, key, &show_avatars);
-
-	empathy_theme_set_show_avatars (priv->theme, show_avatars);
-}
-
-static void
-chat_view_populate_popup (EmpathyChatView *view,
-			  GtkMenu        *menu,
-			  gpointer        user_data)
-{
-	EmpathyChatViewPriv *priv;
-	GtkTextTagTable    *table;
-	GtkTextTag         *tag;
-	gint                x, y;
-	GtkTextIter         iter, start, end;
-	GtkWidget          *item;
-	gchar              *str = NULL;
-
-	priv = GET_PRIV (view);
-
-	/* Clear menu item */
-	if (gtk_text_buffer_get_char_count (priv->buffer) > 0) {
-		item = gtk_menu_item_new ();
-		gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
-		gtk_widget_show (item);
-
-		item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL);
-		gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
-		gtk_widget_show (item);
-
-		g_signal_connect (item,
-				  "activate",
-				  G_CALLBACK (chat_view_clear_view_cb),
-				  view);
-	}
-
-	/* Link context menu items */
-	table = gtk_text_buffer_get_tag_table (priv->buffer);
-	tag = gtk_text_tag_table_lookup (table, "link");
-
-	gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y);
-
-	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
-					       GTK_TEXT_WINDOW_WIDGET,
-					       x, y,
-					       &x, &y);
-
-	gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &iter, x, y);
+static void chat_view_base_init (gpointer klass);
 
-	start = end = iter;
-
-	if (gtk_text_iter_backward_to_tag_toggle (&start, tag) &&
-	    gtk_text_iter_forward_to_tag_toggle (&end, tag)) {
-		str = gtk_text_buffer_get_text (priv->buffer,
-						&start, &end, FALSE);
-	}
-
-	if (G_STR_EMPTY (str)) {
-		g_free (str);
-		return;
-	}
-
-	/* NOTE: Set data just to get the string freed when not needed. */
-	g_object_set_data_full (G_OBJECT (menu),
-				"url", str,
-				(GDestroyNotify) g_free);
-
-	item = gtk_menu_item_new ();
-	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
-	gtk_widget_show (item);
-
-	item = gtk_menu_item_new_with_mnemonic (_("_Copy Link Address"));
-	g_signal_connect (item,
-			  "activate",
-			  G_CALLBACK (chat_view_copy_address_cb),
-			  str);
-	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
-	gtk_widget_show (item);
-
-	item = gtk_menu_item_new_with_mnemonic (_("_Open Link"));
-	g_signal_connect (item,
-			  "activate",
-			  G_CALLBACK (chat_view_open_address_cb),
-			  str);
-	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
-	gtk_widget_show (item);
-}
-
-static gboolean
-chat_view_event_cb (EmpathyChatView *view,
-		    GdkEventMotion *event,
-		    GtkTextTag     *tag)
-{
-	static GdkCursor  *hand = NULL;
-	static GdkCursor  *beam = NULL;
-	GtkTextWindowType  type;
-	GtkTextIter        iter;
-	GdkWindow         *win;
-	gint               x, y, buf_x, buf_y;
-
-	type = gtk_text_view_get_window_type (GTK_TEXT_VIEW (view),
-					      event->window);
-
-	if (type != GTK_TEXT_WINDOW_TEXT) {
-		return FALSE;
-	}
-
-	/* Get where the pointer really is. */
-	win = gtk_text_view_get_window (GTK_TEXT_VIEW (view), type);
-	if (!win) {
-		return FALSE;
-	}
-
-	gdk_window_get_pointer (win, &x, &y, NULL);
-
-	/* Get the iter where the cursor is at */
-	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view), type,
-					       x, y,
-					       &buf_x, &buf_y);
-
-	gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view),
-					    &iter,
-					    buf_x, buf_y);
-
-	if (gtk_text_iter_has_tag (&iter, tag)) {
-		if (!hand) {
-			hand = gdk_cursor_new (GDK_HAND2);
-			beam = gdk_cursor_new (GDK_XTERM);
-		}
-		gdk_window_set_cursor (win, hand);
-	} else {
-		if (!beam) {
-			beam = gdk_cursor_new (GDK_XTERM);
-		}
-		gdk_window_set_cursor (win, beam);
-	}
-
-	return FALSE;
-}
-
-static gboolean
-chat_view_url_event_cb (GtkTextTag    *tag,
-			GObject       *object,
-			GdkEvent      *event,
-			GtkTextIter   *iter,
-			GtkTextBuffer *buffer)
+GType
+empathy_chat_view_get_type (void)
 {
-	GtkTextIter  start, end;
-	gchar       *str;
-
-	/* If the link is being selected, don't do anything. */
-	gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
-	if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end)) {
-		return FALSE;
-	}
-
-	if (event->type == GDK_BUTTON_RELEASE && event->button.button == 1) {
-		start = end = *iter;
-
-		if (gtk_text_iter_backward_to_tag_toggle (&start, tag) &&
-		    gtk_text_iter_forward_to_tag_toggle (&end, tag)) {
-			str = gtk_text_buffer_get_text (buffer,
-							&start,
-							&end,
-							FALSE);
-
-			empathy_url_show (str);
-			g_free (str);
-		}
-	}
-
-	return FALSE;
-}
-
-static void
-chat_view_open_address_cb (GtkMenuItem *menuitem, const gchar *url)
-{
-	empathy_url_show (url);
-}
-
-static void
-chat_view_copy_address_cb (GtkMenuItem *menuitem, const gchar *url)
-{
-	GtkClipboard *clipboard;
-
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-	gtk_clipboard_set_text (clipboard, url, -1);
-
-	clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
-	gtk_clipboard_set_text (clipboard, url, -1);
-}
-
-static void
-chat_view_clear_view_cb (GtkMenuItem *menuitem, EmpathyChatView *view)
-{
-	empathy_chat_view_clear (view);
-}
-
-static gboolean
-chat_view_is_scrolled_down (EmpathyChatView *view)
-{
-	GtkWidget *sw;
-
-	sw = gtk_widget_get_parent (GTK_WIDGET (view));
-	if (GTK_IS_SCROLLED_WINDOW (sw)) {
-		GtkAdjustment *vadj;
-
-		vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw));
-
-		if (vadj->value + vadj->page_size / 2 < vadj->upper - vadj->page_size) {
-			return FALSE;
-		}
-	}
-
-	return TRUE;
-}
-
-static void
-chat_view_maybe_trim_buffer (EmpathyChatView *view)
-{
-	EmpathyChatViewPriv *priv;
-	GtkTextIter         top, bottom;
-	gint                line;
-	gint                remove;
-	GtkTextTagTable    *table;
-	GtkTextTag         *tag;
-
-	priv = GET_PRIV (view);
-
-	gtk_text_buffer_get_end_iter (priv->buffer, &bottom);
-	line = gtk_text_iter_get_line (&bottom);
-	if (line < MAX_LINES) {
-		return;
-	}
-
-	remove = line - MAX_LINES;
-	gtk_text_buffer_get_start_iter (priv->buffer, &top);
-
-	bottom = top;
-	if (!gtk_text_iter_forward_lines (&bottom, remove)) {
-		return;
-	}
-
-	/* Track backwords to a place where we can safely cut, we don't do it in
-	 * the middle of a tag.
-	 */
-	table = gtk_text_buffer_get_tag_table (priv->buffer);
-	tag = gtk_text_tag_table_lookup (table, "cut");
-	if (!tag) {
-		return;
-	}
-
-	if (!gtk_text_iter_forward_to_tag_toggle (&bottom, tag)) {
-		return;
-	}
-
-	if (!gtk_text_iter_equal (&top, &bottom)) {
-		gtk_text_buffer_delete (priv->buffer, &top, &bottom);
+	static GType type = 0;
+	
+	if (!type) {
+		static const GTypeInfo type_info = {
+			sizeof (EmpathyChatViewIface),
+			chat_view_base_init,
+			NULL,
+		};
+		
+		type = g_type_register_static (G_TYPE_INTERFACE,
+					       "EmpathyChatView",
+					       &type_info, 0);
+		
+		g_type_interface_add_prerequisite (type, GTK_TYPE_WIDGET);
 	}
+	
+	return type;
 }
 
 static void
-chat_view_theme_changed_cb (EmpathyThemeManager *manager,
-			    EmpathyChatView     *view)
-{
-	EmpathyChatViewPriv *priv;
-	gboolean            show_avatars = FALSE;
-
-	priv = GET_PRIV (view);
-
-	empathy_theme_manager_apply_saved (manager, view);
-
-	/* Needed for now to update the "rise" property of the names to get it
-	 * vertically centered.
-	 */
-	empathy_conf_get_bool (empathy_conf_get (),
-			       EMPATHY_PREFS_UI_SHOW_AVATARS,
-			       &show_avatars);
-	empathy_theme_set_show_avatars (priv->theme, show_avatars);
-}
-
-/* Pads a pixbuf to the specified size, by centering it in a larger transparent
- * pixbuf. Returns a new ref.
- */
-static GdkPixbuf *
-chat_view_pad_to_size (GdkPixbuf *pixbuf,
-		       gint       width,
-		       gint       height,
-		       gint       extra_padding_right)
-{
-	gint       src_width, src_height;
-	GdkPixbuf *padded;
-	gint       x_offset, y_offset;
-
-	src_width = gdk_pixbuf_get_width (pixbuf);
-	src_height = gdk_pixbuf_get_height (pixbuf);
-
-	x_offset = (width - src_width) / 2;
-	y_offset = (height - src_height) / 2;
-
-	padded = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (pixbuf),
-				 TRUE, /* alpha */
-				 gdk_pixbuf_get_bits_per_sample (pixbuf),
-				 width + extra_padding_right,
-				 height);
-
-	gdk_pixbuf_fill (padded, 0);
-
-	gdk_pixbuf_copy_area (pixbuf,
-			      0, /* source coords */
-			      0,
-			      src_width,
-			      src_height,
-			      padded,
-			      x_offset, /* dest coords */
-			      y_offset);
-
-	return padded;
-}
-
-typedef struct {
-	GdkPixbuf *pixbuf;
-	gchar     *token;
-} AvatarData;
-
-static void
-chat_view_avatar_cache_data_free (gpointer ptr)
-{
-	AvatarData *data = ptr;
-
-	g_object_unref (data->pixbuf);
-	g_free (data->token);
-	g_slice_free (AvatarData, data);
-}
-
-GdkPixbuf *
-empathy_chat_view_get_avatar_pixbuf_with_cache (EmpathyContact *contact)
+chat_view_base_init (gpointer klass)
 {
-	AvatarData        *data;
-	EmpathyAvatar     *avatar;
-	GdkPixbuf         *tmp_pixbuf;
-	GdkPixbuf         *pixbuf = NULL;
-
-	/* Check if avatar is in cache and if it's up to date */
-	avatar = empathy_contact_get_avatar (contact);
-	data = g_object_get_data (G_OBJECT (contact), "chat-view-avatar-cache");
-	if (data) {
-		if (avatar && !tp_strdiff (avatar->token, data->token)) {
-			/* We have the avatar in cache */
-			return data->pixbuf;
-		}
-	}
-
-	/* Avatar not in cache, create pixbuf */
-	tmp_pixbuf = empathy_pixbuf_avatar_from_contact_scaled (contact, 32, 32);
-	if (tmp_pixbuf) {
-		pixbuf = chat_view_pad_to_size (tmp_pixbuf, 32, 32, 6);
-		g_object_unref (tmp_pixbuf);
-	}
-	if (!pixbuf) {
-		return NULL;
-	}
-
-	/* Insert new pixbuf in cache */
-	data = g_slice_new0 (AvatarData);
-	data->token = g_strdup (avatar->token);
-	data->pixbuf = pixbuf;
-
-	g_object_set_data_full (G_OBJECT (contact), "chat-view-avatar-cache",
-				data, chat_view_avatar_cache_data_free);
-
-	return data->pixbuf;
-}
-
-EmpathyChatView *
-empathy_chat_view_new (void)
-{
-	return g_object_new (EMPATHY_TYPE_CHAT_VIEW, NULL);
-}
-
-/* Code stolen from pidgin/gtkimhtml.c */
-static gboolean
-chat_view_scroll_cb (EmpathyChatView *view)
-{
-	EmpathyChatViewPriv *priv;
-	GtkAdjustment      *adj;
-	gdouble             max_val;
-
-	priv = GET_PRIV (view);
-	adj = GTK_TEXT_VIEW (view)->vadjustment;
-	max_val = adj->upper - adj->page_size;
-
-	g_return_val_if_fail (priv->scroll_time != NULL, FALSE);
-
-	if (g_timer_elapsed (priv->scroll_time, NULL) > MAX_SCROLL_TIME) {
-		/* time's up. jump to the end and kill the timer */
-		gtk_adjustment_set_value (adj, max_val);
-		g_timer_destroy (priv->scroll_time);
-		priv->scroll_time = NULL;
-		priv->scroll_timeout = 0;
-		return FALSE;
-	}
-
-	/* scroll by 1/3rd the remaining distance */
-	gtk_adjustment_set_value (adj, gtk_adjustment_get_value (adj) + ((max_val - gtk_adjustment_get_value (adj)) / 3));
-	return TRUE;
-}
-
-void
-empathy_chat_view_scroll_down (EmpathyChatView *view)
-{
-	EmpathyChatViewPriv *priv;
-
-	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-
-	priv = GET_PRIV (view);
-
-	if (!priv->allow_scrolling) {
-		return;
-	}
-
-	DEBUG ("Scrolling down");
-
-	if (priv->scroll_time) {
-		g_timer_reset (priv->scroll_time);
-	} else {
-		priv->scroll_time = g_timer_new();
-	}
-	if (!priv->scroll_timeout) {
-		priv->scroll_timeout = g_timeout_add (SCROLL_DELAY,
-						      (GSourceFunc) chat_view_scroll_cb,
-						      view);
+	static gboolean initialized = FALSE;
+	
+	if (!initialized) {
+		initialized = TRUE;
 	}
 }
 
@@ -795,464 +63,113 @@ void
 empathy_chat_view_append_message (EmpathyChatView *view,
 				  EmpathyMessage  *msg)
 {
-	EmpathyChatViewPriv *priv = GET_PRIV (view);
-	gboolean             bottom;
-
 	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-	g_return_if_fail (EMPATHY_IS_MESSAGE (msg));
-
-	if (!empathy_message_get_body (msg)) {
-		return;
-	}
-
-	bottom = chat_view_is_scrolled_down (view);
 	
-	chat_view_maybe_trim_buffer (view);
-
-	empathy_theme_append_message (priv->theme, view, msg);
-
-	if (bottom) {
-		empathy_chat_view_scroll_down (view);
-	}
-
-	if (priv->last_contact) {
-		g_object_unref (priv->last_contact);
+	if (EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->append_message) {
+		EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->append_message (view, 
+									 msg);
 	}
-	priv->last_contact = g_object_ref (empathy_message_get_sender (msg));
 }
 
 void
 empathy_chat_view_append_event (EmpathyChatView *view,
-			       const gchar    *str)
+				const gchar    *str)
 {
-	EmpathyChatViewPriv *priv;
-	gboolean            bottom;
-
 	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-	g_return_if_fail (!G_STR_EMPTY (str));
-
-	priv = GET_PRIV (view);
-
-	bottom = chat_view_is_scrolled_down (view);
-
-	chat_view_maybe_trim_buffer (view);
-
-	empathy_theme_append_event (priv->theme, view, str);
-
-	if (bottom) {
-		empathy_chat_view_scroll_down (view);
-	}
-
-	if (priv->last_contact) {
-		g_object_unref (priv->last_contact);
-		priv->last_contact = NULL;
+	
+	if (EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->append_event) {
+		EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->append_event (view,
+								       str);
 	}
 }
 
 void
-empathy_chat_view_append_button (EmpathyChatView *view,
-				const gchar    *message,
-				GtkWidget      *button1,
-				GtkWidget      *button2)
+empathy_chat_view_scroll (EmpathyChatView *view,
+			  gboolean        allow_scrolling)
 {
-	EmpathyChatViewPriv   *priv;
-	GtkTextChildAnchor   *anchor;
-	GtkTextIter           iter;
-	gboolean              bottom;
-	const gchar          *tag;
-
 	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-	g_return_if_fail (button1 != NULL);
-
-	priv = GET_PRIV (view);
-
-	tag = "invite";
-
-	bottom = chat_view_is_scrolled_down (view);
-
-	empathy_theme_append_timestamp (priv->theme, view, NULL, TRUE, TRUE);
-
-	if (message) {
-		empathy_theme_append_text (priv->theme, view, message, tag, NULL);
-	}
-
-	gtk_text_buffer_get_end_iter (priv->buffer, &iter);
-
-	anchor = gtk_text_buffer_create_child_anchor (priv->buffer, &iter);
-	gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view), button1, anchor);
-	gtk_widget_show (button1);
-
-	gtk_text_buffer_insert_with_tags_by_name (priv->buffer,
-						  &iter,
-						  " ",
-						  1,
-						  tag,
-						  NULL);
-
-	if (button2) {
-		gtk_text_buffer_get_end_iter (priv->buffer, &iter);
-		
-		anchor = gtk_text_buffer_create_child_anchor (priv->buffer, &iter);
-		gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view), button2, anchor);
-		gtk_widget_show (button2);
-		
-		gtk_text_buffer_insert_with_tags_by_name (priv->buffer,
-							  &iter,
-							  " ",
-							  1,
-							  tag,
-							  NULL);
-	}
-
-	gtk_text_buffer_get_end_iter (priv->buffer, &iter);
-	gtk_text_buffer_insert_with_tags_by_name (priv->buffer,
-						  &iter,
-						  "\n\n",
-						  2,
-						  tag,
-						  NULL);
-
-	if (bottom) {
-		empathy_chat_view_scroll_down (view);
-	}
-
-	if (priv->last_contact) {
-		g_object_unref (priv->last_contact);
-		priv->last_contact = NULL;
+	
+	if (EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->scroll) {
+		EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->scroll (view, 
+								 allow_scrolling);
 	}
 }
 
 void
-empathy_chat_view_scroll (EmpathyChatView *view,
-			 gboolean        allow_scrolling)
+empathy_chat_view_scroll_down (EmpathyChatView *view)
 {
-	EmpathyChatViewPriv *priv = GET_PRIV (view);
-
 	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-
-	DEBUG ("Scrolling %s", allow_scrolling ? "enabled" : "disabled");
-
-	priv->allow_scrolling = allow_scrolling;
-	if (allow_scrolling) {
-		empathy_chat_view_scroll_down (view);
+	
+	if (EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->scroll_down) {
+		EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->scroll_down (view);
 	}
 }
 
 gboolean
-empathy_chat_view_get_selection_bounds (EmpathyChatView *view,
-				       GtkTextIter    *start,
-				       GtkTextIter    *end)
+empathy_chat_view_get_has_selection (EmpathyChatView *view)
 {
-	GtkTextBuffer *buffer;
-
 	g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), FALSE);
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-
-	return gtk_text_buffer_get_selection_bounds (buffer, start, end);
+	
+	if (EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->get_has_selection) {
+		return EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->get_has_selection (view);
+	}
+	return FALSE;
 }
 
 void
 empathy_chat_view_clear (EmpathyChatView *view)
 {
-	GtkTextBuffer      *buffer;
-	EmpathyChatViewPriv *priv;
-
 	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-	gtk_text_buffer_set_text (buffer, "", -1);
-
-	/* We set these back to the initial values so we get
-	 * timestamps when clearing the window to know when
-	 * conversations start.
-	 */
-	priv = GET_PRIV (view);
-
-	priv->last_timestamp = 0;
+	
+	if (EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->clear) {
+		EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->clear (view);
+	}
 }
 
 gboolean
 empathy_chat_view_find_previous (EmpathyChatView *view,
-				const gchar    *search_criteria,
-				gboolean        new_search)
+				 const gchar    *search_criteria,
+				 gboolean        new_search)
 {
-	EmpathyChatViewPriv *priv;
-	GtkTextBuffer      *buffer;
-	GtkTextIter         iter_at_mark;
-	GtkTextIter         iter_match_start;
-	GtkTextIter         iter_match_end;
-	gboolean            found;
-	gboolean            from_start = FALSE;
-
 	g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), FALSE);
-	g_return_val_if_fail (search_criteria != NULL, FALSE);
-
-	priv = GET_PRIV (view);
-
-	buffer = priv->buffer;
-
-	if (G_STR_EMPTY (search_criteria)) {
-		if (priv->find_mark_previous) {
-			gtk_text_buffer_get_start_iter (buffer, &iter_at_mark);
-
-			gtk_text_buffer_move_mark (buffer,
-						   priv->find_mark_previous,
-						   &iter_at_mark);
-			gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
-						      priv->find_mark_previous,
-						      0.0,
-						      TRUE,
-						      0.0,
-						      0.0);
-			gtk_text_buffer_select_range (buffer,
-						      &iter_at_mark,
-						      &iter_at_mark);
-		}
-
-		return FALSE;
-	}
-
-	if (new_search) {
-		from_start = TRUE;
-	}
-
-	if (priv->find_mark_previous) {
-		gtk_text_buffer_get_iter_at_mark (buffer,
-						  &iter_at_mark,
-						  priv->find_mark_previous);
-	} else {
-		gtk_text_buffer_get_end_iter (buffer, &iter_at_mark);
-		from_start = TRUE;
-	}
-
-	priv->find_last_direction = FALSE;
-
-	found = empathy_text_iter_backward_search (&iter_at_mark,
-						  search_criteria,
-						  &iter_match_start,
-						  &iter_match_end,
-						  NULL);
-
-	if (!found) {
-		gboolean result = FALSE;
-
-		if (from_start) {
-			return result;
-		}
-
-		/* Here we wrap around. */
-		if (!new_search && !priv->find_wrapped) {
-			priv->find_wrapped = TRUE;
-			result = empathy_chat_view_find_previous (view, 
-								 search_criteria, 
-								 FALSE);
-			priv->find_wrapped = FALSE;
-		}
-
-		return result;
-	}
-
-	/* Set new mark and show on screen */
-	if (!priv->find_mark_previous) {
-		priv->find_mark_previous = gtk_text_buffer_create_mark (buffer, NULL,
-									&iter_match_start,
-									TRUE);
-	} else {
-		gtk_text_buffer_move_mark (buffer,
-					   priv->find_mark_previous,
-					   &iter_match_start);
-	}
-
-	if (!priv->find_mark_next) {
-		priv->find_mark_next = gtk_text_buffer_create_mark (buffer, NULL,
-								    &iter_match_end,
-								    TRUE);
-	} else {
-		gtk_text_buffer_move_mark (buffer,
-					   priv->find_mark_next,
-					   &iter_match_end);
+	
+	if (EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->find_previous) {
+		return EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->find_previous (view, 
+									       search_criteria, 
+									       new_search);
 	}
-
-	gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
-				      priv->find_mark_previous,
-				      0.0,
-				      TRUE,
-				      0.5,
-				      0.5);
-
-	gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &iter_match_start);
-	gtk_text_buffer_move_mark_by_name (buffer, "insert", &iter_match_end);
-
-	return TRUE;
+	return FALSE;
 }
 
 gboolean
 empathy_chat_view_find_next (EmpathyChatView *view,
-			    const gchar    *search_criteria,
-			    gboolean        new_search)
+			     const gchar    *search_criteria,
+			     gboolean        new_search)
 {
-	EmpathyChatViewPriv *priv;
-	GtkTextBuffer      *buffer;
-	GtkTextIter         iter_at_mark;
-	GtkTextIter         iter_match_start;
-	GtkTextIter         iter_match_end;
-	gboolean            found;
-	gboolean            from_start = FALSE;
-
 	g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), FALSE);
-	g_return_val_if_fail (search_criteria != NULL, FALSE);
-
-	priv = GET_PRIV (view);
-
-	buffer = priv->buffer;
-
-	if (G_STR_EMPTY (search_criteria)) {
-		if (priv->find_mark_next) {
-			gtk_text_buffer_get_start_iter (buffer, &iter_at_mark);
-
-			gtk_text_buffer_move_mark (buffer,
-						   priv->find_mark_next,
-						   &iter_at_mark);
-			gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
-						      priv->find_mark_next,
-						      0.0,
-						      TRUE,
-						      0.0,
-						      0.0);
-			gtk_text_buffer_select_range (buffer,
-						      &iter_at_mark,
-						      &iter_at_mark);
-		}
-
-		return FALSE;
-	}
-
-	if (new_search) {
-		from_start = TRUE;
-	}
-
-	if (priv->find_mark_next) {
-		gtk_text_buffer_get_iter_at_mark (buffer,
-						  &iter_at_mark,
-						  priv->find_mark_next);
-	} else {
-		gtk_text_buffer_get_start_iter (buffer, &iter_at_mark);
-		from_start = TRUE;
-	}
-
-	priv->find_last_direction = TRUE;
-
-	found = empathy_text_iter_forward_search (&iter_at_mark,
-						 search_criteria,
-						 &iter_match_start,
-						 &iter_match_end,
-						 NULL);
-
-	if (!found) {
-		gboolean result = FALSE;
-
-		if (from_start) {
-			return result;
-		}
-
-		/* Here we wrap around. */
-		if (!new_search && !priv->find_wrapped) {
-			priv->find_wrapped = TRUE;
-			result = empathy_chat_view_find_next (view, 
-							     search_criteria, 
-							     FALSE);
-			priv->find_wrapped = FALSE;
-		}
-
-		return result;
-	}
-
-	/* Set new mark and show on screen */
-	if (!priv->find_mark_next) {
-		priv->find_mark_next = gtk_text_buffer_create_mark (buffer, NULL,
-							       &iter_match_end,
-							       TRUE);
-	} else {
-		gtk_text_buffer_move_mark (buffer,
-					   priv->find_mark_next,
-					   &iter_match_end);
-	}
-
-	if (!priv->find_mark_previous) {
-		priv->find_mark_previous = gtk_text_buffer_create_mark (buffer, NULL,
-									&iter_match_start,
-									TRUE);
-	} else {
-		gtk_text_buffer_move_mark (buffer,
-					   priv->find_mark_previous,
-					   &iter_match_start);
+	
+	if (EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->find_next) {
+		return EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->find_next (view, 
+									   search_criteria, 
+									   new_search);
 	}
-
-	gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
-				      priv->find_mark_next,
-				      0.0,
-				      TRUE,
-				      0.5,
-				      0.5);
-
-	gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &iter_match_start);
-	gtk_text_buffer_move_mark_by_name (buffer, "insert", &iter_match_end);
-
-	return TRUE;
+	return FALSE;
 }
 
 
 void
 empathy_chat_view_find_abilities (EmpathyChatView *view,
-				 const gchar    *search_criteria,
-				 gboolean       *can_do_previous,
-				 gboolean       *can_do_next)
+				  const gchar    *search_criteria,
+				  gboolean       *can_do_previous,
+				  gboolean       *can_do_next)
 {
-	EmpathyChatViewPriv *priv;
-	GtkTextBuffer      *buffer;
-	GtkTextIter         iter_at_mark;
-	GtkTextIter         iter_match_start;
-	GtkTextIter         iter_match_end;
-
 	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-	g_return_if_fail (search_criteria != NULL);
-	g_return_if_fail (can_do_previous != NULL && can_do_next != NULL);
-
-	priv = GET_PRIV (view);
-
-	buffer = priv->buffer;
-
-	if (can_do_previous) {
-		if (priv->find_mark_previous) {
-			gtk_text_buffer_get_iter_at_mark (buffer,
-							  &iter_at_mark,
-							  priv->find_mark_previous);
-		} else {
-			gtk_text_buffer_get_start_iter (buffer, &iter_at_mark);
-		}
-		
-		*can_do_previous = empathy_text_iter_backward_search (&iter_at_mark,
-								     search_criteria,
-								     &iter_match_start,
-								     &iter_match_end,
-								     NULL);
-	}
-
-	if (can_do_next) {
-		if (priv->find_mark_next) {
-			gtk_text_buffer_get_iter_at_mark (buffer,
-							  &iter_at_mark,
-							  priv->find_mark_next);
-		} else {
-			gtk_text_buffer_get_start_iter (buffer, &iter_at_mark);
-		}
-		
-		*can_do_next = empathy_text_iter_forward_search (&iter_at_mark,
-								search_criteria,
-								&iter_match_start,
-								&iter_match_end,
-								NULL);
+	
+	if (EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->find_abilities) {
+		EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->find_abilities (view, 
+									 search_criteria, 
+									 can_do_previous, 
+									 can_do_next);
 	}
 }
 
@@ -1260,209 +177,20 @@ void
 empathy_chat_view_highlight (EmpathyChatView *view,
 			     const gchar     *text)
 {
-	GtkTextBuffer *buffer;
-	GtkTextIter    iter;
-	GtkTextIter    iter_start;
-	GtkTextIter    iter_end;
-	GtkTextIter    iter_match_start;
-	GtkTextIter    iter_match_end;
-	gboolean       found;
-
 	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-
-	gtk_text_buffer_get_start_iter (buffer, &iter);
-
-	gtk_text_buffer_get_bounds (buffer, &iter_start, &iter_end);
-	gtk_text_buffer_remove_tag_by_name (buffer, "highlight",
-					    &iter_start,
-					    &iter_end);
-
-	if (G_STR_EMPTY (text)) {
-		return;
-	}
-
-	while (1) {
-		found = empathy_text_iter_forward_search (&iter,
-							 text,
-							 &iter_match_start,
-							 &iter_match_end,
-							 NULL);
-
-		if (!found) {
-			break;
-		}
-
-		gtk_text_buffer_apply_tag_by_name (buffer, "highlight",
-						   &iter_match_start,
-						   &iter_match_end);
-
-		iter = iter_match_end;
-		gtk_text_iter_forward_char (&iter);
+	
+	if (EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->highlight) {
+		EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->highlight (view, text);
 	}
 }
 
 void
 empathy_chat_view_copy_clipboard (EmpathyChatView *view)
 {
-	GtkTextBuffer *buffer;
-	GtkClipboard  *clipboard;
-
-	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-
-	gtk_text_buffer_copy_clipboard (buffer, clipboard);
-}
-
-EmpathyTheme *
-empathy_chat_view_get_theme (EmpathyChatView *view)
-{
-	EmpathyChatViewPriv *priv;
-
-	g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), NULL);
-
-	priv = GET_PRIV (view);
-
-	return priv->theme;
-}
-
-static void
-chat_view_theme_notify_cb (EmpathyTheme    *theme,
-			   GParamSpec      *param,
-			   EmpathyChatView *view)
-{
-	empathy_theme_update_view (theme, view);
-}
-
-void
-empathy_chat_view_set_theme (EmpathyChatView *view, EmpathyTheme *theme)
-{
-	EmpathyChatViewPriv *priv;
-
 	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-	g_return_if_fail (EMPATHY_IS_THEME (theme));
-
-	priv = GET_PRIV (view);
-
-	if (priv->theme) {
-		g_signal_handlers_disconnect_by_func (priv->theme,
-						      chat_view_theme_notify_cb,
-						      view);
-		g_object_unref (priv->theme);
-	}
-
-	priv->theme = g_object_ref (theme);
-
-	empathy_theme_update_view (theme, view);
-	g_signal_connect (priv->theme, "notify",
-			  G_CALLBACK (chat_view_theme_notify_cb),
-			  view);
-
-	/* FIXME: Redraw all messages using the new theme */
-}
-
-void
-empathy_chat_view_set_margin (EmpathyChatView *view,
-			     gint            margin)
-{
-	EmpathyChatViewPriv *priv;
-
-	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-
-	priv = GET_PRIV (view);
-
-	g_object_set (view,
-		      "left-margin", margin,
-		      "right-margin", margin,
-		      NULL);
-}
-
-GtkWidget *
-empathy_chat_view_get_smiley_menu (GCallback    callback,
-				   gpointer     user_data)
-{
-	EmpathySmileyManager *smiley_manager;
-	GSList               *smileys, *l;
-	GtkWidget            *menu;
-	gint                  x = 0;
-	gint                  y = 0;
-
-	g_return_val_if_fail (callback != NULL, NULL);
-
-	menu = gtk_menu_new ();
-
-	smiley_manager = empathy_smiley_manager_new ();
-	smileys = empathy_smiley_manager_get_all (smiley_manager);
-	for (l = smileys; l; l = l->next) {
-		EmpathySmiley *smiley;
-		GtkWidget     *item;
-		GtkWidget     *image;
-
-		smiley = l->data;
-		image = gtk_image_new_from_pixbuf (smiley->pixbuf);
-
-		item = gtk_image_menu_item_new_with_label ("");
-		gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
-
-		gtk_menu_attach (GTK_MENU (menu), item,
-				 x, x + 1, y, y + 1);
-
-		gtk_widget_set_tooltip_text (item, smiley->str);
-
-		g_object_set_data  (G_OBJECT (item), "smiley_text", smiley->str);
-		g_signal_connect (item, "activate", callback, user_data);
-
-		if (x > 3) {
-			y++;
-			x = 0;
-		} else {
-			x++;
-		}
+	
+	if (EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->copy_clipboard) {
+		EMPATHY_TYPE_CHAT_VIEW_GET_IFACE (view)->copy_clipboard (view);
 	}
-	g_object_unref (smiley_manager);
-
-	gtk_widget_show_all (menu);
-
-	return menu;
-}
-
-time_t
-empathy_chat_view_get_last_timestamp (EmpathyChatView *view)
-{
-	EmpathyChatViewPriv *priv;
-
-	g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), 0);
-
-	priv = GET_PRIV (view);
-
-	return priv->last_timestamp;
-}
-
-void
-empathy_chat_view_set_last_timestamp (EmpathyChatView *view,
-				     time_t          timestamp)
-{
-	EmpathyChatViewPriv *priv;
-
-	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-
-	priv = GET_PRIV (view);
-
-	priv->last_timestamp = timestamp;
-}
-
-EmpathyContact *
-empathy_chat_view_get_last_contact (EmpathyChatView *view)
-{
-	EmpathyChatViewPriv *priv;
-
-	g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), NULL);
-
-	priv = GET_PRIV (view);
-
-	return priv->last_contact;
 }
 
diff --git a/libempathy-gtk/empathy-chat-view.h b/libempathy-gtk/empathy-chat-view.h
index 0c54abd..08fba15 100644
--- a/libempathy-gtk/empathy-chat-view.h
+++ b/libempathy-gtk/empathy-chat-view.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * Copyright (C) 2002-2007 Imendio AB
+ * Copyright (C) 2008 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -17,9 +17,7 @@
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  * 
- * Authors: Mikael Hallendal <micke@imendio.com>
- *          Richard Hult <richard@imendio.com>
- *          Martyn Russell <martyn@imendio.com>
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
  */
 
 #ifndef __EMPATHY_CHAT_VIEW_H__
@@ -34,41 +32,49 @@ G_BEGIN_DECLS
 
 #define EMPATHY_TYPE_CHAT_VIEW         (empathy_chat_view_get_type ())
 #define EMPATHY_CHAT_VIEW(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CHAT_VIEW, EmpathyChatView))
-#define EMPATHY_CHAT_VIEW_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CHAT_VIEW, EmpathyChatViewClass))
 #define EMPATHY_IS_CHAT_VIEW(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CHAT_VIEW))
-#define EMPATHY_IS_CHAT_VIEW_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CHAT_VIEW))
-#define EMPATHY_CHAT_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CHAT_VIEW, EmpathyChatViewClass))
+#define EMPATHY_TYPE_CHAT_VIEW_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), EMPATHY_TYPE_CHAT_VIEW, EmpathyChatViewIface))
 
 typedef struct _EmpathyChatView      EmpathyChatView;
-typedef struct _EmpathyChatViewClass EmpathyChatViewClass;
+typedef struct _EmpathyChatViewIface EmpathyChatViewIface;
 
-#include "empathy-theme.h"
+struct _EmpathyChatViewIface {
+	GTypeInterface   base_iface;
 
-struct _EmpathyChatView {
-	GtkTextView parent;
-	gpointer priv;
-};
-
-struct _EmpathyChatViewClass {
-	GtkTextViewClass parent_class;
+	/* VTabled */
+	void             (*append_message)       (EmpathyChatView *view,
+						  EmpathyMessage  *msg);
+	void             (*append_event)         (EmpathyChatView *view,
+						  const gchar     *str);
+	void             (*scroll)               (EmpathyChatView *view,
+						  gboolean         allow_scrolling);
+	void             (*scroll_down)          (EmpathyChatView *view);
+	gboolean         (*get_has_selection)    (EmpathyChatView *view);
+	void             (*clear)                (EmpathyChatView *view);
+	gboolean         (*find_previous)        (EmpathyChatView *view,
+						  const gchar     *search_criteria,
+						  gboolean         new_search);
+	gboolean         (*find_next)            (EmpathyChatView *view,
+						  const gchar     *search_criteria,
+						  gboolean         new_search);
+	void             (*find_abilities)       (EmpathyChatView *view,
+						  const gchar     *search_criteria,
+						  gboolean        *can_do_previous,
+						  gboolean        *can_do_next);
+	void             (*highlight)            (EmpathyChatView *view,
+						  const gchar     *text);
+	void             (*copy_clipboard)       (EmpathyChatView *view);
 };
 
 GType            empathy_chat_view_get_type             (void) G_GNUC_CONST;
-EmpathyChatView *empathy_chat_view_new                  (void);
 void             empathy_chat_view_append_message       (EmpathyChatView *view,
 							 EmpathyMessage  *msg);
 void             empathy_chat_view_append_event         (EmpathyChatView *view,
 							 const gchar     *str);
-void             empathy_chat_view_append_button        (EmpathyChatView *view,
-							 const gchar     *message,
-							 GtkWidget       *button1,
-							 GtkWidget       *button2);
 void             empathy_chat_view_scroll               (EmpathyChatView *view,
 							 gboolean         allow_scrolling);
 void             empathy_chat_view_scroll_down          (EmpathyChatView *view);
-gboolean         empathy_chat_view_get_selection_bounds (EmpathyChatView *view,
-							 GtkTextIter     *start,
-							 GtkTextIter     *end);
+gboolean         empathy_chat_view_get_has_selection    (EmpathyChatView *view);
 void             empathy_chat_view_clear                (EmpathyChatView *view);
 gboolean         empathy_chat_view_find_previous        (EmpathyChatView *view,
 							 const gchar     *search_criteria,
@@ -83,19 +89,8 @@ void             empathy_chat_view_find_abilities       (EmpathyChatView *view,
 void             empathy_chat_view_highlight            (EmpathyChatView *view,
 							 const gchar     *text);
 void             empathy_chat_view_copy_clipboard       (EmpathyChatView *view);
-EmpathyTheme *   empathy_chat_view_get_theme            (EmpathyChatView *view);
-void             empathy_chat_view_set_theme            (EmpathyChatView *view,
-							 EmpathyTheme    *theme);
-void             empathy_chat_view_set_margin           (EmpathyChatView *view,
-							 gint             margin);
-GtkWidget *      empathy_chat_view_get_smiley_menu      (GCallback        callback,
-							 gpointer         user_data);
-time_t           empathy_chat_view_get_last_timestamp   (EmpathyChatView *view);
-void             empathy_chat_view_set_last_timestamp   (EmpathyChatView *view,
-							 time_t           timestamp);
-EmpathyContact * empathy_chat_view_get_last_contact     (EmpathyChatView *view);
-GdkPixbuf *      empathy_chat_view_get_avatar_pixbuf_with_cache (EmpathyContact *contact);
 
 G_END_DECLS
 
 #endif /* __EMPATHY_CHAT_VIEW_H__ */
+
diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c
index 1dd3908..a2c5cec 100644
--- a/libempathy-gtk/empathy-chat.c
+++ b/libempathy-gtk/empathy-chat.c
@@ -48,6 +48,8 @@
 #include "empathy-contact-list-store.h"
 #include "empathy-contact-list-view.h"
 #include "empathy-contact-menu.h"
+#include "empathy-theme-manager.h"
+#include "empathy-smiley-manager.h"
 #include "empathy-ui-utils.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_CHAT
@@ -866,19 +868,18 @@ chat_input_realize_cb (GtkWidget   *widget,
 }
 
 static void
-chat_insert_smiley_activate_cb (GtkWidget   *menuitem,
-				EmpathyChat *chat)
+chat_insert_smiley_activate_cb (EmpathySmileyManager *manager,
+				EmpathySmiley        *smiley,
+				gpointer              user_data)
 {
+	EmpathyChat   *chat = EMPATHY_CHAT (user_data);
 	GtkTextBuffer *buffer;
 	GtkTextIter    iter;
-	const gchar   *smiley;
-
-	smiley = g_object_get_data (G_OBJECT (menuitem), "smiley_text");
 
 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
 
 	gtk_text_buffer_get_end_iter (buffer, &iter);
-	gtk_text_buffer_insert (buffer, &iter, smiley, -1);
+	gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
 
 	gtk_text_buffer_get_end_iter (buffer, &iter);
 	gtk_text_buffer_insert (buffer, &iter, " ", -1);
@@ -940,17 +941,18 @@ chat_input_populate_popup_cb (GtkTextView *view,
 			      GtkMenu     *menu,
 			      EmpathyChat *chat)
 {
-	EmpathyChatPriv  *priv;
-	GtkTextBuffer   *buffer;
-	GtkTextTagTable *table;
-	GtkTextTag      *tag;
-	gint             x, y;
-	GtkTextIter      iter, start, end;
-	GtkWidget       *item;
-	gchar           *str = NULL;
-	EmpathyChatSpell *chat_spell;
-	GtkWidget       *smiley_menu;
-	GtkWidget       *image;
+	EmpathyChatPriv      *priv;
+	GtkTextBuffer        *buffer;
+	GtkTextTagTable      *table;
+	GtkTextTag           *tag;
+	gint                  x, y;
+	GtkTextIter           iter, start, end;
+	GtkWidget            *item;
+	gchar                *str = NULL;
+	EmpathyChatSpell      *chat_spell;
+	EmpathySmileyManager *smiley_manager;
+	GtkWidget            *smiley_menu;
+	GtkWidget            *image;
 
 	priv = GET_PRIV (chat);
 	buffer = gtk_text_view_get_buffer (view);
@@ -967,10 +969,12 @@ chat_input_populate_popup_cb (GtkTextView *view,
 	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
 	gtk_widget_show (item);
 
-	smiley_menu = empathy_chat_view_get_smiley_menu (
-		G_CALLBACK (chat_insert_smiley_activate_cb),
-		chat);
+	smiley_manager = empathy_smiley_manager_new ();
+	smiley_menu = empathy_smiley_menu_new (smiley_manager,
+					       chat_insert_smiley_activate_cb,
+					       chat);
 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smiley_menu);
+	g_object_unref (smiley_manager);
 
 	/* Add the Send menu item. */
 	gtk_text_buffer_get_bounds (buffer, &start, &end);
@@ -1264,8 +1268,8 @@ chat_create_ui (EmpathyChat *chat)
 	g_free (filename);
 	g_object_unref (glade);
 
-	/* Add message GtkTextView. */
-	chat->view = empathy_chat_view_new ();
+	/* Add message view. */
+	chat->view = empathy_theme_manager_create_view (empathy_theme_manager_get ());
 	g_signal_connect (chat->view, "focus_in_event",
 			  G_CALLBACK (chat_text_view_focus_in_event_cb),
 			  chat);
@@ -1729,7 +1733,7 @@ empathy_chat_cut (EmpathyChat *chat)
 	g_return_if_fail (EMPATHY_IS_CHAT (chat));
 
 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
-	if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL)) {
+	if (gtk_text_buffer_get_has_selection (buffer)) {
 		GtkClipboard *clipboard;
 
 		clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
@@ -1745,13 +1749,13 @@ empathy_chat_copy (EmpathyChat *chat)
 
 	g_return_if_fail (EMPATHY_IS_CHAT (chat));
 
-	if (empathy_chat_view_get_selection_bounds (chat->view, NULL, NULL)) {
+	if (empathy_chat_view_get_has_selection (chat->view)) {
 		empathy_chat_view_copy_clipboard (chat->view);
 		return;
 	}
 
 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
-	if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL)) {
+	if (gtk_text_buffer_get_has_selection (buffer)) {
 		GtkClipboard *clipboard;
 
 		clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
diff --git a/libempathy-gtk/empathy-conf.h b/libempathy-gtk/empathy-conf.h
index 2da820b..6318a52 100644
--- a/libempathy-gtk/empathy-conf.h
+++ b/libempathy-gtk/empathy-conf.h
@@ -51,6 +51,7 @@ struct _EmpathyConfClass {
 #define EMPATHY_PREFS_POPUPS_WHEN_AVAILABLE        EMPATHY_PREFS_PATH "/notifications/popups_when_available"
 #define EMPATHY_PREFS_CHAT_SHOW_SMILEYS            EMPATHY_PREFS_PATH "/conversation/graphical_smileys"
 #define EMPATHY_PREFS_CHAT_THEME                   EMPATHY_PREFS_PATH "/conversation/theme"
+#define EMPATHY_PREFS_CHAT_ADIUM_PATH              EMPATHY_PREFS_PATH "/conversation/adium_path"
 #define EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES EMPATHY_PREFS_PATH "/conversation/spell_checker_languages"
 #define EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED   EMPATHY_PREFS_PATH "/conversation/spell_checker_enabled"
 #define EMPATHY_PREFS_CHAT_NICK_COMPLETION_CHAR    EMPATHY_PREFS_PATH "/conversation/nick_completion_char"
diff --git a/libempathy-gtk/empathy-log-window.c b/libempathy-gtk/empathy-log-window.c
index f1d958a..d44f1ed 100644
--- a/libempathy-gtk/empathy-log-window.c
+++ b/libempathy-gtk/empathy-log-window.c
@@ -41,6 +41,7 @@
 #include "empathy-log-window.h"
 #include "empathy-account-chooser.h"
 #include "empathy-chat-view.h"
+#include "empathy-theme-manager.h"
 #include "empathy-ui-utils.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
@@ -207,13 +208,13 @@ empathy_log_window_show (McAccount   *account,
 			  window);
 
 	/* Configure Search EmpathyChatView */
-	window->chatview_find = empathy_chat_view_new ();
+	window->chatview_find = empathy_theme_manager_create_view (empathy_theme_manager_get ());
 	gtk_container_add (GTK_CONTAINER (window->scrolledwindow_find),
 			   GTK_WIDGET (window->chatview_find));
 	gtk_widget_show (GTK_WIDGET (window->chatview_find));
 
 	/* Configure Contacts EmpathyChatView */
-	window->chatview_chats = empathy_chat_view_new ();
+	window->chatview_chats = empathy_theme_manager_create_view (empathy_theme_manager_get ());
 	gtk_container_add (GTK_CONTAINER (window->scrolledwindow_chats),
 			   GTK_WIDGET (window->chatview_chats));
 	gtk_widget_show (GTK_WIDGET (window->chatview_chats));
diff --git a/libempathy-gtk/empathy-smiley-manager.c b/libempathy-gtk/empathy-smiley-manager.c
index c9322c8..99d3521 100644
--- a/libempathy-gtk/empathy-smiley-manager.c
+++ b/libempathy-gtk/empathy-smiley-manager.c
@@ -37,9 +37,10 @@ typedef struct {
 } EmpathySmileyManagerPriv;
 
 struct _SmileyManagerTree {
-	gunichar   c;
-	GdkPixbuf *pixbuf;
-	GSList    *childrens;
+	gunichar     c;
+	GdkPixbuf   *pixbuf;
+	const gchar *path;
+	GSList      *childrens;
 };
 
 G_DEFINE_TYPE (EmpathySmileyManager, empathy_smiley_manager, G_TYPE_OBJECT);
@@ -53,6 +54,7 @@ smiley_manager_tree_new (gunichar c)
 	tree->c = c;
 	tree->pixbuf = NULL;
 	tree->childrens = NULL;
+	tree->path = NULL;
 
 	return tree;
 }
@@ -78,7 +80,7 @@ smiley_manager_tree_free (SmileyManagerTree *tree)
 }
 
 static EmpathySmiley *
-smiley_new (GdkPixbuf *pixbuf, const gchar *str)
+smiley_new (GdkPixbuf *pixbuf, const gchar *str, const gchar *path)
 {
 	EmpathySmiley *smiley;
 
@@ -87,6 +89,7 @@ smiley_new (GdkPixbuf *pixbuf, const gchar *str)
 		smiley->pixbuf = g_object_ref (pixbuf);
 	}
 	smiley->str = g_strdup (str);
+	smiley->path = path;
 
 	return smiley;
 }
@@ -109,9 +112,16 @@ static void
 smiley_manager_finalize (GObject *object)
 {
 	EmpathySmileyManagerPriv *priv = GET_PRIV (object);
+	GSList                   *l;
 
 	smiley_manager_tree_free (priv->tree);
-	g_slist_foreach (priv->smileys, (GFunc) empathy_smiley_free, NULL);
+	for (l = priv->smileys; l; l = l->next) {
+		EmpathySmiley *smiley = l->data;
+
+		/* The smiley got the ownership of the path */
+		g_free ((gchar*) smiley->path);
+		empathy_smiley_free (smiley);
+	}
 	g_slist_free (priv->smileys);
 }
 
@@ -185,8 +195,9 @@ smiley_manager_tree_find_or_insert_child (SmileyManagerTree *tree, gunichar c)
 
 static void
 smiley_manager_tree_insert (SmileyManagerTree *tree,
-			    GdkPixbuf         *smiley,
-			    const gchar       *str)
+			    GdkPixbuf         *pixbuf,
+			    const gchar       *str,
+			    const gchar       *path)
 {
 	SmileyManagerTree *child;
 
@@ -194,27 +205,32 @@ smiley_manager_tree_insert (SmileyManagerTree *tree,
 
 	str = g_utf8_next_char (str);
 	if (*str) {
-		smiley_manager_tree_insert (child, smiley, str);
+		smiley_manager_tree_insert (child, pixbuf, str, path);
 		return;
 	}
 
-	child->pixbuf = g_object_ref (smiley);
+	child->pixbuf = g_object_ref (pixbuf);
+	child->path = path;
 }
 
 static void
 smiley_manager_add_valist (EmpathySmileyManager *manager,
-			   GdkPixbuf            *smiley,
+			   GdkPixbuf            *pixbuf,
+			   gchar                *path,
 			   const gchar          *first_str,
 			   va_list               var_args)
 {
 	EmpathySmileyManagerPriv *priv = GET_PRIV (manager);
 	const gchar              *str;
+	EmpathySmiley            *smiley;
 
 	for (str = first_str; str; str = va_arg (var_args, gchar*)) {
-		smiley_manager_tree_insert (priv->tree, smiley, str);
+		smiley_manager_tree_insert (priv->tree, pixbuf, str, path);
 	}
 
-	priv->smileys = g_slist_prepend (priv->smileys, smiley_new (smiley, first_str));
+	/* We give the ownership of path to the smiley */
+	smiley = smiley_new (pixbuf, first_str, path);
+	priv->smileys = g_slist_prepend (priv->smileys, smiley);
 }
 
 void
@@ -223,40 +239,26 @@ empathy_smiley_manager_add (EmpathySmileyManager *manager,
 			    const gchar          *first_str,
 			    ...)
 {
-	GdkPixbuf *smiley;
+	GdkPixbuf *pixbuf;
 	va_list    var_args;
 
 	g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager));
 	g_return_if_fail (!G_STR_EMPTY (icon_name));
 	g_return_if_fail (!G_STR_EMPTY (first_str));
 
-	smiley = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
-	if (smiley) {
+	pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
+	if (pixbuf) {
+		gchar *path;
+
 		va_start (var_args, first_str);
-		smiley_manager_add_valist (manager, smiley, first_str, var_args);
+		path = empathy_filename_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
+		smiley_manager_add_valist (manager, pixbuf, path, first_str, var_args);
 		va_end (var_args);
-		g_object_unref (smiley);
+		g_object_unref (pixbuf);
 	}
 }
 
 void
-empathy_smiley_manager_add_from_pixbuf (EmpathySmileyManager *manager,
-					GdkPixbuf            *smiley,
-					const gchar          *first_str,
-					...)
-{
-	va_list var_args;
-
-	g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager));
-	g_return_if_fail (GDK_IS_PIXBUF (smiley));
-	g_return_if_fail (!G_STR_EMPTY (first_str));
-
-	va_start (var_args, first_str);
-	smiley_manager_add_valist (manager, smiley, first_str, var_args);
-	va_end (var_args);
-}
-
-void
 empathy_smiley_manager_load (EmpathySmileyManager *manager)
 {
 	g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager));
@@ -303,7 +305,9 @@ empathy_smiley_manager_parse (EmpathySmileyManager *manager,
 		if (cur_tree == priv->tree) {
 			if (child) {
 				if (t > cur_str) {
-					smiley = smiley_new (NULL, g_strndup (cur_str, t - cur_str));
+					smiley = smiley_new (NULL,
+							     g_strndup (cur_str, t - cur_str),
+							     NULL);
 					smileys = g_slist_prepend (smileys, smiley);
 				}
 				cur_str = t;
@@ -318,7 +322,9 @@ empathy_smiley_manager_parse (EmpathySmileyManager *manager,
 			continue;
 		}
 
-		smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str));
+		smiley = smiley_new (cur_tree->pixbuf,
+				     g_strndup (cur_str, t - cur_str),
+				     cur_tree->path);
 		smileys = g_slist_prepend (smileys, smiley);
 		if (cur_tree->pixbuf) {
 			cur_str = t;
@@ -333,7 +339,9 @@ empathy_smiley_manager_parse (EmpathySmileyManager *manager,
 		}
 	}
 
-	smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str));
+	smiley = smiley_new (cur_tree->pixbuf,
+			     g_strndup (cur_str, t - cur_str),
+			     cur_tree->path);
 	smileys = g_slist_prepend (smileys, smiley);
 
 	return g_slist_reverse (smileys);
@@ -347,3 +355,87 @@ empathy_smiley_manager_get_all (EmpathySmileyManager *manager)
 	return priv->smileys;
 }
 
+typedef struct {
+	EmpathySmileyManager *manager;
+	EmpathySmiley        *smiley;
+	EmpathySmileyMenuFunc func;    
+	gpointer              user_data;         
+} ActivateData;
+
+static void
+smiley_menu_data_free (gpointer  user_data,
+		       GClosure *closure)
+{
+	ActivateData *data = (ActivateData*) user_data;
+
+	g_object_unref (data->manager);
+	g_slice_free (ActivateData, data);
+}
+
+static void
+smiley_menu_activate_cb (GtkMenuItem *menuitem,
+			 gpointer     user_data)
+{
+	ActivateData *data = (ActivateData*) user_data;
+
+	data->func (data->manager, data->smiley, data->user_data);
+}
+
+GtkWidget *
+empathy_smiley_menu_new (EmpathySmileyManager *manager,
+			 EmpathySmileyMenuFunc func,
+			 gpointer              user_data)
+{
+	EmpathySmileyManagerPriv *priv = GET_PRIV (manager);
+	GSList                   *l;
+	GtkWidget                *menu;
+	gint                      x = 0;
+	gint                      y = 0;
+
+	g_return_val_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager), NULL);
+	g_return_val_if_fail (func != NULL, NULL);
+
+	menu = gtk_menu_new ();
+
+	for (l = priv->smileys; l; l = l->next) {
+		EmpathySmiley *smiley;
+		GtkWidget     *item;
+		GtkWidget     *image;
+		ActivateData  *data;
+
+		smiley = l->data;
+		image = gtk_image_new_from_pixbuf (smiley->pixbuf);
+
+		item = gtk_image_menu_item_new_with_label ("");
+		gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+
+		gtk_menu_attach (GTK_MENU (menu), item,
+				 x, x + 1, y, y + 1);
+
+		gtk_widget_set_tooltip_text (item, smiley->str);
+
+		data = g_slice_new (ActivateData);
+		data->manager = g_object_ref (manager);
+		data->smiley = smiley;
+		data->func = func;
+		data->user_data = user_data;
+
+		g_signal_connect_data (item, "activate",
+				       G_CALLBACK (smiley_menu_activate_cb),
+				       data,
+				       smiley_menu_data_free,
+				       0);
+
+		if (x > 3) {
+			y++;
+			x = 0;
+		} else {
+			x++;
+		}
+	}
+
+	gtk_widget_show_all (menu);
+
+	return menu;
+}
+
diff --git a/libempathy-gtk/empathy-smiley-manager.h b/libempathy-gtk/empathy-smiley-manager.h
index 394215e..318369f 100644
--- a/libempathy-gtk/empathy-smiley-manager.h
+++ b/libempathy-gtk/empathy-smiley-manager.h
@@ -48,10 +48,15 @@ struct _EmpathySmileyManagerClass {
 };
 
 typedef struct {
-	GdkPixbuf *pixbuf;
-	gchar     *str;
+	GdkPixbuf   *pixbuf;
+	gchar       *str;
+	const gchar *path;
 } EmpathySmiley;
 
+typedef void (*EmpathySmileyMenuFunc) (EmpathySmileyManager *manager,
+				       EmpathySmiley        *smiley,
+				       gpointer              user_data);
+
 GType                 empathy_smiley_manager_get_type        (void) G_GNUC_CONST;
 EmpathySmileyManager *empathy_smiley_manager_new             (void);
 void                  empathy_smiley_manager_load            (EmpathySmileyManager *manager);
@@ -59,13 +64,12 @@ void                  empathy_smiley_manager_add             (EmpathySmileyManag
 							      const gchar          *icon_name,
 							      const gchar          *first_str,
 							      ...);
-void                  empathy_smiley_manager_add_from_pixbuf (EmpathySmileyManager *manager,
-							      GdkPixbuf            *smiley,
-							      const gchar          *first_str,
-							      ...);
 GSList *              empathy_smiley_manager_get_all         (EmpathySmileyManager *manager);
 GSList *              empathy_smiley_manager_parse           (EmpathySmileyManager *manager,
 							      const gchar          *text);
+GtkWidget *           empathy_smiley_menu_new                (EmpathySmileyManager *manager,
+							      EmpathySmileyMenuFunc func,
+							      gpointer              user_data);
 void                  empathy_smiley_free                    (EmpathySmiley        *smiley);
 
 G_END_DECLS
diff --git a/libempathy-gtk/empathy-theme-adium-manager.c b/libempathy-gtk/empathy-theme-adium-manager.c
new file mode 100644
index 0000000..056ecb3
--- /dev/null
+++ b/libempathy-gtk/empathy-theme-adium-manager.c
@@ -0,0 +1,223 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <telepathy-glib/util.h>
+#include <libempathy/empathy-utils.h>
+
+#include "empathy-theme-manager.h"
+#include "empathy-chat-view.h"
+#include "empathy-conf.h"
+#include "empathy-chat-text-view.h"
+#include "empathy-theme-boxes.h"
+#include "empathy-theme-irc.h"
+
+#ifdef HAVE_WEBKIT
+#include "empathy-theme-adium.h"
+#include "empathy-theme-adium-manager.h"
+#endif
+
+#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
+#include <libempathy/empathy-debug.h>
+
+#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeAdiumManager)
+typedef struct {
+	GList       *themes;
+} EmpathyThemeAdiumManagerPriv;
+
+enum {
+	LAST_SIGNAL
+};
+
+//static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (EmpathyThemeAdiumManager, empathy_theme_adium_manager, G_TYPE_OBJECT);
+
+static void
+theme_adium_manager_finalize (GObject *object)
+{
+	EmpathyThemeAdiumManagerPriv *priv = GET_PRIV (object);
+	EmpathyThemeAdiumDetails *details = NULL;
+	GList *iter = NULL;
+	
+	iter = g_list_first (priv->themes);
+	while (iter) {
+		details = (EmpathyThemeAdiumDetails*) iter->data;
+		g_free (details->name);
+		g_free (details->path);
+		g_free (details);
+		iter = g_list_next (iter);
+	}
+	G_OBJECT_CLASS (empathy_theme_adium_manager_parent_class)->finalize (object);
+}
+
+static void
+empathy_theme_adium_manager_class_init (EmpathyThemeAdiumManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+/*
+	signals[THEME_CHANGED] =
+		g_signal_new ("theme-changed",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+*/
+	g_type_class_add_private (object_class, sizeof (EmpathyThemeAdiumManagerPriv));
+
+	object_class->finalize = theme_adium_manager_finalize;
+}
+
+static 
+void element(GMarkupParseContext *context,
+	const gchar	*text,
+	gsize	text_len,
+	gpointer user_data,
+	GError	**error)
+{
+	gchar* value = g_strndup (text, text_len);
+	value = g_strstrip(value);
+
+	EmpathyThemeAdiumDetails *details = (EmpathyThemeAdiumDetails*) user_data;
+	if (g_strcmp0(details->name, "") == 0) {
+		details->name = value; 
+		return;
+	} else if (strcmp ("CFBundleName", value) == 0) 
+		details->name = "";
+	g_free (value);
+}
+
+/* The list of what handler does what. */
+static GMarkupParser parser = {
+	NULL,
+	NULL,
+	element,
+	NULL,
+	NULL
+};
+
+static EmpathyThemeAdiumDetails *
+create_theme (const gchar *dirpath)
+{
+	gchar *filename;
+	filename = g_build_filename (dirpath, "Contents", "Info.plist", NULL);
+	if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+		g_free (filename);
+		return NULL;
+	}
+
+	EmpathyThemeAdiumDetails *details = g_new0 (EmpathyThemeAdiumDetails, 1);
+	GMarkupParseContext *context = g_markup_parse_context_new (
+		&parser,
+		0,
+		details,
+		NULL);
+
+	char *text;
+	gsize length;
+	if (g_file_get_contents (filename, &text, &length, NULL) == FALSE) {
+		g_free (filename);
+		g_warning ("Couldn't load %s\n", filename);
+		return NULL;
+	}
+	
+	if (g_markup_parse_context_parse (context, text, length, NULL) == FALSE) {
+		g_free (filename);
+		g_warning ("Couldn't parse %s\n", filename);
+		return NULL;
+	}
+	
+	g_free(text);
+	g_markup_parse_context_free (context);
+
+	if (details->name != NULL) {
+		details->path = g_strdup (dirpath);
+	} else {
+		g_free (details);
+		details = NULL;
+	}
+
+	g_free (filename);
+	return details;
+}
+
+static void
+find_themes (GList **list, const gchar *dirpath)
+{
+	GDir *dir;
+	GError *error = NULL;
+	const gchar *name = NULL;
+	gchar *path = NULL;
+	EmpathyThemeAdiumDetails *details = NULL;
+
+	dir = g_dir_open (dirpath, 0, &error);
+	if (dir) {
+		do {
+			name = g_dir_read_name (dir);
+			path = g_build_filename (dirpath, name, NULL);
+			if (g_file_test (path, G_FILE_TEST_IS_DIR)) {
+				details = create_theme (path);
+			}
+			g_free (path);
+			if (details) 
+				*list = g_list_append (*list, details);
+
+		}
+		while (name); 
+		g_dir_close(dir);
+	} else {
+		g_warning("Error opening %s: %s\n", ADIUMTHEMESPATH, error->message);
+	}
+}
+
+
+static void
+empathy_theme_adium_manager_init (EmpathyThemeAdiumManager *manager)
+{
+	EmpathyThemeAdiumManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
+		EMPATHY_TYPE_THEME_ADIUM_MANAGER, EmpathyThemeAdiumManagerPriv);
+
+	manager->priv = priv;
+	priv->themes = NULL;
+	
+	gchar* userdir = g_build_filename (g_get_home_dir (), ".empathy", "themes", NULL);
+	// Build theme list
+	find_themes (&priv->themes, ADIUMTHEMESPATH);
+	find_themes (&priv->themes, userdir);
+	g_free (userdir);
+}
+
+GList*
+empathy_theme_adium_manager_get_themes (EmpathyThemeAdiumManager* manager)
+{
+	EmpathyThemeAdiumManagerPriv *priv = GET_PRIV (manager);
+	return priv->themes;
+}
diff --git a/libempathy-gtk/empathy-theme-adium-manager.h b/libempathy-gtk/empathy-theme-adium-manager.h
new file mode 100644
index 0000000..1324d00
--- /dev/null
+++ b/libempathy-gtk/empathy-theme-adium-manager.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Pierre-Luc Beaudoin <pierre-luc@pierlux.com> 
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
+ */
+
+#ifndef __EMPATHY_THEME_ADIUM_MANAGER_H__
+#define __EMPATHY_THEME_ADIUM_MANAGER_H__
+
+#include <glib-object.h>
+#include "empathy-chat-view.h"
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_THEME_ADIUM_MANAGER         (empathy_theme_adium_manager_get_type ())
+#define EMPATHY_THEME_ADIUM_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_THEME_ADIUM_MANAGER, EmpathyThemeAdiumManager))
+#define EMPATHY_THEME_ADIUM_MANAGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_THEME_ADIUM_MANAGER, EmpathyThemeAdiumManagerClass))
+#define EMPATHY_IS_THEME_ADIUM_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_THEME_ADIUM_MANAGER))
+#define EMPATHY_IS_THEME_ADIUM_MANAGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_THEME_ADIUM_MANAGER))
+#define EMPATHY_THEME_ADIUM_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_THEME_ADIUM_MANAGER, EmpathyThemeAdiumManagerClass))
+
+typedef struct _EmpathyThemeAdiumManager      EmpathyThemeAdiumManager;
+typedef struct _EmpathyThemeAdiumManagerClass EmpathyThemeAdiumManagerClass;
+
+struct _EmpathyThemeAdiumManager {
+	GObject parent;
+	gpointer priv;
+};
+
+struct _EmpathyThemeAdiumManagerClass {
+	GObjectClass parent_class;
+};
+
+typedef struct {
+	gchar *name;
+	gchar *path;
+} EmpathyThemeAdiumDetails;
+
+
+GType                   empathy_theme_adium_manager_get_type    (void) G_GNUC_CONST;
+EmpathyThemeAdiumManager *   empathy_theme_adium_manager_get         (void);
+GList*          empathy_theme_adium_manager_get_themes  (EmpathyThemeAdiumManager* manager);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_THEME_ADIUM_MANAGER_H__ */
diff --git a/libempathy-gtk/empathy-theme-adium.c b/libempathy-gtk/empathy-theme-adium.c
new file mode 100644
index 0000000..2d028f0
--- /dev/null
+++ b/libempathy-gtk/empathy-theme-adium.c
@@ -0,0 +1,795 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Collabora Ltd.
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include <webkit/webkitnetworkrequest.h>
+
+#include <libempathy/empathy-time.h>
+#include <libempathy/empathy-utils.h>
+
+#include "empathy-theme-adium.h"
+#include "empathy-smiley-manager.h"
+#include "empathy-conf.h"
+#include "empathy-ui-utils.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
+#include <libempathy/empathy-debug.h>
+
+#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeAdium)
+
+typedef struct {
+	EmpathySmileyManager *smiley_manager;
+	EmpathyContact       *last_contact;
+	gboolean              page_loaded;
+	GList                *message_queue;
+	gchar                *path;
+	gchar                *default_avatar_filename;
+	gchar                *template_html;
+	gchar                *basedir;
+	gchar                *in_content_html;
+	gsize                 in_content_len;
+	gchar                *in_nextcontent_html;
+	gsize                 in_nextcontent_len;
+	gchar                *out_content_html;
+	gsize                 out_content_len;
+	gchar                *out_nextcontent_html;
+	gsize                 out_nextcontent_len;
+	gchar                *status_html;
+	gsize                 status_len;
+} EmpathyThemeAdiumPriv;
+
+static void theme_adium_iface_init (EmpathyChatViewIface *iface);
+
+enum {
+	PROP_0,
+	PROP_PATH,
+};
+
+G_DEFINE_TYPE_WITH_CODE (EmpathyThemeAdium, empathy_theme_adium,
+			 WEBKIT_TYPE_WEB_VIEW,
+			 G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CHAT_VIEW,
+						theme_adium_iface_init));
+
+static void
+theme_adium_load (EmpathyThemeAdium *theme)
+{
+	EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
+	gchar                 *file;
+	gchar                 *template_html = NULL;
+	gsize                  template_len;
+	GString               *string;
+	gchar                **strv = NULL;
+	gchar                 *css_path;
+	guint                  len = 0;
+	guint                  i = 0;
+
+	priv->basedir = g_strconcat (priv->path, G_DIR_SEPARATOR_S "Contents" G_DIR_SEPARATOR_S "Resources" G_DIR_SEPARATOR_S, NULL);
+
+	/* Load html files */
+	file = g_build_filename (priv->basedir, "Incoming", "Content.html", NULL);
+	g_file_get_contents (file, &priv->in_content_html, &priv->in_content_len, NULL);
+	g_free (file);
+
+	file = g_build_filename (priv->basedir, "Incoming", "NextContent.html", NULL);
+	g_file_get_contents (file, &priv->in_nextcontent_html, &priv->in_nextcontent_len, NULL);
+	g_free (file);
+
+	file = g_build_filename (priv->basedir, "Outgoing", "Content.html", NULL);
+	g_file_get_contents (file, &priv->out_content_html, &priv->out_content_len, NULL);
+	g_free (file);
+
+	file = g_build_filename (priv->basedir, "Outgoing", "NextContent.html", NULL);
+	g_file_get_contents (file, &priv->out_nextcontent_html, &priv->out_nextcontent_len, NULL);
+	g_free (file);
+
+	file = g_build_filename (priv->basedir, "Status.html", NULL);
+	g_file_get_contents (file, &priv->status_html, &priv->status_len, NULL);
+	g_free (file);
+
+	css_path = g_build_filename (priv->basedir, "main.css", NULL);
+
+	/* There is 2 formats for Template.html: The old one has 4 parameters,
+	 * the new one has 5 parameters. */
+	file = g_build_filename (priv->basedir, "Template.html", NULL);
+	if (g_file_get_contents (file, &template_html, &template_len, NULL)) {
+		strv = g_strsplit (template_html, "%@", -1);
+		len = g_strv_length (strv);
+	}
+	g_free (file);
+
+	if (len != 5 && len != 6) {
+		/* Either the theme has no template or it don't have the good
+		 * number of parameters. Fallback to use our own template. */
+		g_free (template_html);
+		g_strfreev (strv);
+
+		file = empathy_file_lookup ("Template.html", "data");
+		g_file_get_contents (file, &template_html, &template_len, NULL);
+		g_free (file);
+		strv = g_strsplit (template_html, "%@", -1);
+		len = g_strv_length (strv);
+	}
+
+	/* Replace %@ with the needed information in the template html. */
+	string = g_string_sized_new (template_len);
+	g_string_append (string, strv[i++]);
+	g_string_append (string, priv->basedir);
+	g_string_append (string, strv[i++]);
+	if (len == 6) {
+		/* We include main.css by default */
+		g_string_append_printf (string, "@import url(\"%s\");", css_path);
+		g_string_append (string, strv[i++]);
+		/* FIXME: We should set the variant css here */
+		g_string_append (string, "");
+	} else {
+		/* FIXME: We should set main.css OR the variant css */
+		g_string_append (string, css_path);
+	}
+	g_string_append (string, strv[i++]);
+	g_string_append (string, ""); /* We don't want header */
+	g_string_append (string, strv[i++]);
+	g_string_append (string, ""); /* FIXME: We don't support footer yet */
+	g_string_append (string, strv[i++]);
+	priv->template_html = g_string_free (string, FALSE);
+
+	/* Load the template */
+	webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (theme),
+					  priv->template_html, priv->basedir);
+
+	g_free (template_html);
+	g_free (css_path);
+	g_strfreev (strv);
+}
+
+static WebKitNavigationResponse
+theme_adium_navigation_requested_cb (WebKitWebView        *view,
+				     WebKitWebFrame       *frame,
+				     WebKitNetworkRequest *request,
+				     gpointer              user_data)
+{
+	const gchar *uri;
+
+	uri = webkit_network_request_get_uri (request);
+	empathy_url_show (uri);
+
+	return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
+}
+
+static void
+theme_adium_populate_popup_cb (WebKitWebView *view,
+			       GtkMenu       *menu,
+			       gpointer       user_data)
+{
+	GtkWidget *item;
+
+	/* Remove default menu items */
+	gtk_container_foreach (GTK_CONTAINER (menu),
+		(GtkCallback) gtk_widget_destroy, NULL);
+	
+	/* Select all item */
+	item = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
+	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+	gtk_widget_show (item);
+		
+	g_signal_connect_swapped (item, "activate",
+				  G_CALLBACK (webkit_web_view_select_all),
+				  view);
+
+	/* Copy menu item */
+	if (webkit_web_view_can_copy_clipboard (view)) {
+		item = gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY, NULL);
+		gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+		gtk_widget_show (item);
+		
+		g_signal_connect_swapped (item, "activate",
+					  G_CALLBACK (webkit_web_view_copy_clipboard),
+					  view);
+	}
+
+	/* Clear menu item */
+	item = gtk_separator_menu_item_new ();
+	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+	gtk_widget_show (item);
+		
+	item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL);
+	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+	gtk_widget_show (item);
+		
+	g_signal_connect_swapped (item, "activate",
+				  G_CALLBACK (empathy_chat_view_clear),
+				  view);
+
+	/* FIXME: Add open_link and copy_link when those bugs are fixed:
+	 * https://bugs.webkit.org/show_bug.cgi?id=16092
+	 * https://bugs.webkit.org/show_bug.cgi?id=16562
+	 */
+}
+
+static gchar *
+theme_adium_parse_body (EmpathyThemeAdium *theme,
+			const gchar       *text)
+{
+	EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
+	gboolean               use_smileys = FALSE;
+	GSList                *smileys, *l;
+	GString               *string;
+	gint                   num_matches, i;
+	GArray                *start, *end;
+	gchar                 *ret = NULL;
+	gint                   prev;
+
+	empathy_conf_get_bool (empathy_conf_get (),
+			       EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
+			       &use_smileys);
+
+	if (use_smileys) {
+		/* Replace smileys by a <img/> tag */
+		string = g_string_sized_new (strlen (text));
+		smileys = empathy_smiley_manager_parse (priv->smiley_manager, text);
+		for (l = smileys; l; l = l->next) {
+			EmpathySmiley *smiley;
+
+			smiley = l->data;
+			if (smiley->path) {
+				g_string_append_printf (string,
+							"<abbr title='%s'><img src=\"%s\"/ alt=\"%s\"/></abbr>",
+							smiley->str, smiley->path, smiley->str);
+			} else {
+				ret = g_markup_escape_text (smiley->str, -1);
+				g_string_append (string, ret);
+				g_free (ret);
+			}
+			empathy_smiley_free (smiley);
+		}
+		g_slist_free (smileys);
+
+		ret = g_string_free (string, FALSE);
+	}
+
+	/* Add <a href></a> arround links */
+	start = g_array_new (FALSE, FALSE, sizeof (gint));
+	end = g_array_new (FALSE, FALSE, sizeof (gint));
+	num_matches = empathy_regex_match (EMPATHY_REGEX_ALL, ret, start, end);
+	if (num_matches > 0) {
+		gint   last = 0;
+		gint   s = 0, e = 0;
+
+		string = g_string_sized_new (strlen (ret));
+		for (i = 0; i < num_matches; i++) {
+			s = g_array_index (start, gint, i);
+			e = g_array_index (end, gint, i);
+
+			if (s > last) {
+				/* Append the text between last link (or the
+				 * start of the message) and this link */
+				g_string_append_len (string, ret + last, s - last);
+			}
+
+			/* Append the link inside <a href=""></a> tag */
+			g_string_append (string, "<a href=\"");
+			g_string_append_len (string, ret + s, e - s);
+			g_string_append (string, "\">");
+			g_string_append_len (string, ret + s, e - s);
+			g_string_append (string, "</a>");
+
+			last = e;
+		}
+
+		if (e < strlen (ret)) {
+			/* Append the text after the last link */
+			g_string_append_len (string, ret + e, strlen (ret) - e);
+		}
+
+		g_free (ret);
+		ret = g_string_free (string, FALSE);
+	}
+	g_array_free (start, TRUE);
+	g_array_free (end, TRUE);
+
+	/* Replace \n by <br/> */
+	string = NULL;
+	prev = 0;
+	for (i = 0; ret[i] != '\0'; i++) {
+		if (ret[i] == '\n') {
+			if (!string ) {
+				string = g_string_sized_new (strlen (ret));
+			}
+			g_string_append_len (string, ret + prev, i - prev);
+			g_string_append (string, "<br/>");
+			prev = i + 1;
+		}
+	}
+	if (string) {
+		g_string_append (string, ret + prev);
+		g_free (ret);
+		ret = g_string_free (string, FALSE);
+	}
+
+	return ret;
+}
+
+static void
+escape_and_append_len (GString *string, const gchar *str, gint len)
+{
+	while (*str != '\0' && len != 0) {
+		switch (*str) {
+		case '\\':
+			/* \ becomes \\ */
+			g_string_append (string, "\\\\");	
+			break;
+		case '\"':
+			/* " becomes \" */
+			g_string_append (string, "\\\"");
+			break;
+		case '\n':
+			/* Remove end of lines */
+			break;
+		default:
+			g_string_append_c (string, *str);
+		}
+
+		str++;
+		len--;
+	}
+}
+
+static gboolean
+theme_adium_match (const gchar **str, const gchar *match)
+{
+	gint len;
+
+	len = strlen (match);
+	if (strncmp (*str, match, len) == 0) {
+		*str += len - 1;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void
+theme_adium_append_html (EmpathyThemeAdium *theme,
+			 const gchar       *func,
+			 const gchar       *html, gsize len,
+		         const gchar       *message,
+		         const gchar       *avatar_filename,
+		         const gchar       *name,
+		         time_t             timestamp)
+{
+	GString     *string;
+	const gchar *cur = NULL;
+	gchar       *script;
+
+	/* Make some search-and-replace in the html code */
+	string = g_string_sized_new (len + strlen (message));
+	g_string_append_printf (string, "%s(\"", func);
+	for (cur = html; *cur != '\0'; cur++) {
+		const gchar *replace = NULL;
+		gchar       *dup_replace = NULL;
+
+		if (theme_adium_match (&cur, "%message%")) {
+			replace = message;
+		} else if (theme_adium_match (&cur, "%userIconPath%")) {
+			replace = avatar_filename;
+		} else if (theme_adium_match (&cur, "%sender%")) {
+			replace = name;
+		} else if (theme_adium_match (&cur, "%time")) {
+			gchar *format = NULL;
+			gchar *end;
+
+			/* Extract the time format if provided. */
+			if (*cur == '{') {
+				end = strstr (cur + 1, "}%");
+				if (!end) {
+					/* Invalid string */
+					continue;
+				}
+				cur++;
+				format = g_strndup (cur, end - cur);
+				cur = end;
+			} else {
+				cur++;
+			}
+
+			dup_replace = empathy_time_to_string_local (timestamp,
+				format ? format : EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
+			replace = dup_replace;
+			g_free (format);
+		} else {
+			escape_and_append_len (string, cur, 1);
+			continue;
+		}
+
+		/* Here we have a replacement to make */
+		escape_and_append_len (string, replace, -1);
+		g_free (dup_replace);
+	}
+	g_string_append (string, "\")");
+
+	script = g_string_free (string, FALSE);
+	webkit_web_view_execute_script (WEBKIT_WEB_VIEW (theme), script);
+	g_free (script);
+}
+
+static void
+theme_adium_append_message (EmpathyChatView *view,
+			    EmpathyMessage  *msg)
+{
+	EmpathyThemeAdium     *theme = EMPATHY_THEME_ADIUM (view);
+	EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
+	EmpathyContact        *sender;
+	gchar                 *dup_body = NULL;
+	const gchar           *body;
+	const gchar           *name;
+	EmpathyAvatar         *avatar;
+	const gchar           *avatar_filename = NULL;
+	time_t                 timestamp;
+	gchar                 *html = NULL;
+	gsize                  len = 0;
+	const gchar           *func;
+
+	if (!priv->page_loaded) {
+		priv->message_queue = g_list_prepend (priv->message_queue,
+						      g_object_ref (msg));
+		return;
+	}
+
+	/* Get information */
+	sender = empathy_message_get_sender (msg);
+	timestamp = empathy_message_get_timestamp (msg);
+	body = empathy_message_get_body (msg);
+	dup_body = theme_adium_parse_body (theme, body);
+	if (dup_body) {
+		body = dup_body;
+	}
+	name = empathy_contact_get_name (sender);
+	avatar = empathy_contact_get_avatar (sender);
+	if (avatar) {
+		avatar_filename = avatar->filename;
+	}
+	if (!avatar_filename) {
+		if (!priv->default_avatar_filename) {
+			priv->default_avatar_filename =
+				empathy_filename_from_icon_name ("stock_person",
+								 GTK_ICON_SIZE_DIALOG);
+		}
+		avatar_filename = priv->default_avatar_filename;
+	}
+
+	/* FIXME: What if the message is an action "/me foo"? */
+	/* Get the right html/func to add the message */
+	if (priv->last_contact &&
+	    empathy_contact_equal (priv->last_contact, sender)) {
+		func = "appendNextMessage";
+		if (empathy_contact_is_user (sender)) {
+			html = priv->out_nextcontent_html;
+			len = priv->out_nextcontent_len;
+		}
+		if (!html) {
+			html = priv->in_nextcontent_html;
+			len = priv->in_nextcontent_len;
+		}
+	}
+	if (!html) {
+		func = "appendMessage";
+		if (empathy_contact_is_user (sender)) {
+			html = priv->out_content_html;
+			len = priv->out_content_len;
+		}
+		if (!html) {
+			html = priv->in_content_html;
+			len = priv->in_content_len;
+		}
+	}
+
+	theme_adium_append_html (theme, func, html, len, body, avatar_filename,
+				 name, timestamp);
+
+	/* Keep the sender of the last displayed message */
+	if (priv->last_contact) {
+		g_object_unref (priv->last_contact);
+	}
+	priv->last_contact = g_object_ref (sender);
+
+	g_free (dup_body);
+}
+
+static void
+theme_adium_append_event (EmpathyChatView *view,
+			  const gchar     *str)
+{
+	EmpathyThemeAdium     *theme = EMPATHY_THEME_ADIUM (view);
+	EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
+
+	if (priv->status_html) {
+		theme_adium_append_html (theme, "appendMessage",
+					 priv->status_html, priv->status_len,
+					 str, NULL, NULL,
+					 empathy_time_get_current ());
+	}
+
+	/* There is no last contact */
+	if (priv->last_contact) {
+		g_object_unref (priv->last_contact);
+		priv->last_contact = NULL;
+	}
+}
+
+static void
+theme_adium_scroll (EmpathyChatView *view,
+		    gboolean         allow_scrolling)
+{
+	/* FIXME: Is it possible? I guess we need a js function, but I don't 
+	 * see any... */
+}
+
+static void
+theme_adium_scroll_down (EmpathyChatView *view)
+{
+	webkit_web_view_execute_script (WEBKIT_WEB_VIEW (view), "scrollToBottom()");
+}
+
+static gboolean
+theme_adium_get_has_selection (EmpathyChatView *view)
+{
+	return webkit_web_view_has_selection (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+theme_adium_clear (EmpathyChatView *view)
+{
+	EmpathyThemeAdiumPriv *priv = GET_PRIV (view);
+
+	priv->page_loaded = FALSE;
+	webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (view),
+					  priv->template_html, priv->basedir);
+}
+
+static gboolean
+theme_adium_find_previous (EmpathyChatView *view,
+			   const gchar     *search_criteria,
+			   gboolean         new_search)
+{
+	return webkit_web_view_search_text (WEBKIT_WEB_VIEW (view),
+					    search_criteria, FALSE,
+					    FALSE, TRUE);
+}
+
+static gboolean
+theme_adium_find_next (EmpathyChatView *view,
+		       const gchar     *search_criteria,
+		       gboolean         new_search)
+{
+	return webkit_web_view_search_text (WEBKIT_WEB_VIEW (view),
+					    search_criteria, FALSE,
+					    TRUE, TRUE);
+}
+
+static void
+theme_adium_find_abilities (EmpathyChatView *view,
+			    const gchar    *search_criteria,
+			    gboolean       *can_do_previous,
+			    gboolean       *can_do_next)
+{
+	/* FIXME: Does webkit provide an API for that? We have wrap=true in
+	 * find_next and find_previous to work around this problem. */
+	if (can_do_previous)
+		*can_do_previous = TRUE;
+	if (can_do_next)
+		*can_do_next = TRUE;
+}
+
+static void
+theme_adium_highlight (EmpathyChatView *view,
+		       const gchar     *text)
+{
+	webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (view));
+	webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (view),
+					   text, FALSE, 0);
+	webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (view),
+						    TRUE);
+}
+
+static void
+theme_adium_copy_clipboard (EmpathyChatView *view)
+{
+	webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+theme_adium_iface_init (EmpathyChatViewIface *iface)
+{
+	iface->append_message = theme_adium_append_message;
+	iface->append_event = theme_adium_append_event;
+	iface->scroll = theme_adium_scroll;
+	iface->scroll_down = theme_adium_scroll_down;
+	iface->get_has_selection = theme_adium_get_has_selection;
+	iface->clear = theme_adium_clear;
+	iface->find_previous = theme_adium_find_previous;
+	iface->find_next = theme_adium_find_next;
+	iface->find_abilities = theme_adium_find_abilities;
+	iface->highlight = theme_adium_highlight;
+	iface->copy_clipboard = theme_adium_copy_clipboard;
+}
+
+static void
+theme_adium_load_finished_cb (WebKitWebView  *view,
+			      WebKitWebFrame *frame,
+			      gpointer        user_data)
+{
+	EmpathyThemeAdiumPriv *priv = GET_PRIV (view);
+	EmpathyChatView       *chat_view = EMPATHY_CHAT_VIEW (view);
+
+	DEBUG ("Page loaded");
+	priv->page_loaded = TRUE;
+
+	/* Display queued messages */
+	priv->message_queue = g_list_reverse (priv->message_queue);
+	while (priv->message_queue) {
+		EmpathyMessage *message = priv->message_queue->data;
+
+		theme_adium_append_message (chat_view, message);
+		priv->message_queue = g_list_remove (priv->message_queue, message);
+		g_object_unref (message);
+	}
+}
+
+static void
+theme_adium_finalize (GObject *object)
+{
+	EmpathyThemeAdiumPriv *priv = GET_PRIV (object);
+
+	g_free (priv->basedir);
+	g_free (priv->template_html);
+	g_free (priv->in_content_html);
+	g_free (priv->in_nextcontent_html);
+	g_free (priv->out_content_html);
+	g_free (priv->out_nextcontent_html);
+	g_free (priv->default_avatar_filename);
+	g_free (priv->path);
+	g_object_unref (priv->smiley_manager);
+
+	if (priv->last_contact) {
+		g_object_unref (priv->last_contact);
+	}
+
+	G_OBJECT_CLASS (empathy_theme_adium_parent_class)->finalize (object);
+}
+
+static void
+theme_adium_constructed (GObject *object)
+{
+	theme_adium_load (EMPATHY_THEME_ADIUM (object));
+}
+
+static void
+theme_adium_get_property (GObject    *object,
+			  guint       param_id,
+			  GValue     *value,
+			  GParamSpec *pspec)
+{
+	EmpathyThemeAdiumPriv *priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_PATH:
+		g_value_set_string (value, priv->path);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+theme_adium_set_property (GObject      *object,
+			  guint         param_id,
+			  const GValue *value,
+			  GParamSpec   *pspec)
+{
+	EmpathyThemeAdiumPriv *priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_PATH:
+		g_free (priv->path);
+		priv->path = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+empathy_theme_adium_class_init (EmpathyThemeAdiumClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	
+	object_class->finalize = theme_adium_finalize;
+	object_class->constructed = theme_adium_constructed;
+	object_class->get_property = theme_adium_get_property;
+	object_class->set_property = theme_adium_set_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_PATH,
+					 g_param_spec_string ("path",
+							      "The theme path",
+							      "Path to the adium theme",
+							      g_get_home_dir (),
+							      G_PARAM_CONSTRUCT_ONLY |
+							      G_PARAM_READWRITE));
+
+
+	g_type_class_add_private (object_class, sizeof (EmpathyThemeAdiumPriv));
+}
+
+static void
+empathy_theme_adium_init (EmpathyThemeAdium *theme)
+{
+	EmpathyThemeAdiumPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (theme,
+		EMPATHY_TYPE_THEME_ADIUM, EmpathyThemeAdiumPriv);
+
+	theme->priv = priv;	
+
+	priv->smiley_manager = empathy_smiley_manager_new ();
+
+	g_signal_connect (theme, "load-finished",
+			  G_CALLBACK (theme_adium_load_finished_cb),
+			  NULL);
+	g_signal_connect (theme, "navigation-requested",
+			  G_CALLBACK (theme_adium_navigation_requested_cb),
+			  NULL);
+	g_signal_connect (theme, "populate-popup",
+			  G_CALLBACK (theme_adium_populate_popup_cb),
+			  NULL);
+}
+
+EmpathyThemeAdium *
+empathy_theme_adium_new (const gchar *path)
+{
+	g_return_val_if_fail (empathy_theme_adium_is_valid (path), NULL);
+
+	return g_object_new (EMPATHY_TYPE_THEME_ADIUM,
+			     "path", path,
+			     NULL);
+}
+
+gboolean
+empathy_theme_adium_is_valid (const gchar *path)
+{
+	gboolean ret;
+	gchar   *file;
+
+	/* We ship a default Template.html as fallback if there is any problem
+	 * with the one inside the theme. The only other required file is
+	 * Content.html for incoming messages (outgoing fallback to use
+	 * incoming). */
+	file = g_build_filename (path, "Contents", "Resources", "Incoming",
+				 "Content.html", NULL);
+	ret = g_file_test (file, G_FILE_TEST_EXISTS);
+	g_free (file);
+
+	return ret;
+}
+
diff --git a/libempathy-gtk/empathy-theme-adium.h b/libempathy-gtk/empathy-theme-adium.h
new file mode 100644
index 0000000..437edfe
--- /dev/null
+++ b/libempathy-gtk/empathy-theme-adium.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Collabora Ltd.
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#ifndef __EMPATHY_THEME_ADIUM_H__
+#define __EMPATHY_THEME_ADIUM_H__
+
+#include <webkit/webkitwebview.h>
+
+#include "empathy-chat-view.h"
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_THEME_ADIUM         (empathy_theme_adium_get_type ())
+#define EMPATHY_THEME_ADIUM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_THEME_ADIUM, EmpathyThemeAdium))
+#define EMPATHY_THEME_ADIUM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_THEME_ADIUM, EmpathyThemeAdiumClass))
+#define EMPATHY_IS_THEME_ADIUM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_THEME_ADIUM))
+#define EMPATHY_IS_THEME_ADIUM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_THEME_ADIUM))
+#define EMPATHY_THEME_ADIUM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_THEME_ADIUM, EmpathyThemeAdiumClass))
+
+typedef struct _EmpathyThemeAdium      EmpathyThemeAdium;
+typedef struct _EmpathyThemeAdiumClass EmpathyThemeAdiumClass;
+
+struct _EmpathyThemeAdium {
+	WebKitWebView parent;
+	gpointer priv;
+};
+
+struct _EmpathyThemeAdiumClass {
+	WebKitWebViewClass parent_class;
+};
+
+GType              empathy_theme_adium_get_type (void) G_GNUC_CONST;
+EmpathyThemeAdium *empathy_theme_adium_new      (const gchar *path);
+gboolean           empathy_theme_adium_is_valid (const gchar *path);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_THEME_ADIUM_H__ */
diff --git a/libempathy-gtk/empathy-theme-boxes.c b/libempathy-gtk/empathy-theme-boxes.c
index 8d3a813..c16a14c 100644
--- a/libempathy-gtk/empathy-theme-boxes.c
+++ b/libempathy-gtk/empathy-theme-boxes.c
@@ -1,6 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2007 Imendio AB
+ * Copyright (C) 2008 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -16,6 +17,8 @@
  * License along with this program; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
  */
 
 #include <config.h>
@@ -25,9 +28,12 @@
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
+#include <telepathy-glib/util.h>
+
 #include <libempathy/empathy-utils.h>
-#include "empathy-ui-utils.h"
 #include "empathy-theme-boxes.h"
+#include "empathy-ui-utils.h"
+#include "empathy-conf.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
 #include <libempathy/empathy-debug.h>
@@ -37,465 +43,118 @@
 
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeBoxes)
 typedef struct {
-	gchar *header_foreground;
-	gchar *header_background;
-	gchar *header_line_background;
-	gchar *text_foreground;
-	gchar *text_background;
-	gchar *action_foreground;
-	gchar *highlight_foreground;
-	gchar *time_foreground;
-	gchar *event_foreground;
-	gchar *invite_foreground;
-	gchar *link_foreground;
+	gboolean show_avatars;
+	guint    notify_show_avatars_id;
 } EmpathyThemeBoxesPriv;
 
-static void     theme_boxes_finalize          (GObject            *object);
-static void     theme_boxes_get_property      (GObject            *object,
-					       guint               param_id,
-					       GValue             *value,
-					       GParamSpec         *pspec);
-static void     theme_boxes_set_property      (GObject            *object,
-					       guint               param_id,
-					       const GValue       *value,
-					       GParamSpec         *pspec);
-static void     theme_boxes_define_theme_tags (EmpathyTheme        *theme,
-					       EmpathyChatView     *view);
-static void     theme_boxes_update_view       (EmpathyTheme        *theme,
-					       EmpathyChatView     *view);
-static void     theme_boxes_append_message    (EmpathyTheme        *theme,
-					       EmpathyChatView     *view,
-					       EmpathyMessage      *message);
-static void     theme_boxes_append_event      (EmpathyTheme        *theme,
-					       EmpathyChatView     *view,
-					       const gchar        *str);
-static void     theme_boxes_append_timestamp  (EmpathyTheme        *theme,
-					       EmpathyChatView     *view,
-					       EmpathyMessage      *message,
-					       gboolean            show_date,
-					       gboolean            show_time);
-static void     theme_boxes_append_spacing    (EmpathyTheme        *theme,
-					       EmpathyChatView     *view);
-
-enum {
-	PROP_0,
-	PROP_HEADER_FOREGROUND,
-	PROP_HEADER_BACKGROUND,
-	PROP_HEADER_LINE_BACKGROUND,
-	PROP_TEXT_FOREGROUND,
-	PROP_TEXT_BACKGROUND,
-	PROP_ACTION_FOREGROUND,
-	PROP_HIGHLIGHT_FOREGROUND,
-	PROP_TIME_FOREGROUND,
-	PROP_EVENT_FOREGROUND,
-	PROP_INVITE_FOREGROUND,
-	PROP_LINK_FOREGROUND
-};
-
-enum {
-	PROP_FLOP,
-	PROP_MY_PROP
-};
-
-G_DEFINE_TYPE (EmpathyThemeBoxes, empathy_theme_boxes, EMPATHY_TYPE_THEME);
+G_DEFINE_TYPE (EmpathyThemeBoxes, empathy_theme_boxes, EMPATHY_TYPE_CHAT_TEXT_VIEW);
 
 static void
-empathy_theme_boxes_class_init (EmpathyThemeBoxesClass *class)
+theme_boxes_create_tags (EmpathyThemeBoxes *theme)
 {
-	GObjectClass     *object_class;
-	EmpathyThemeClass *theme_class;
+	GtkTextBuffer *buffer;
 
-	object_class = G_OBJECT_CLASS (class);
-	theme_class  = EMPATHY_THEME_CLASS (class);
-
-	object_class->finalize       = theme_boxes_finalize;
-	object_class->get_property   = theme_boxes_get_property;
-	object_class->set_property   = theme_boxes_set_property;
-
-	theme_class->update_view      = theme_boxes_update_view;
-	theme_class->append_message   = theme_boxes_append_message;
-	theme_class->append_event     = theme_boxes_append_event;
-	theme_class->append_timestamp = theme_boxes_append_timestamp;
-	theme_class->append_spacing   = theme_boxes_append_spacing;
-
-	g_object_class_install_property (object_class,
-					 PROP_HEADER_FOREGROUND,
-					 g_param_spec_string ("header-foreground",
-							      "",
-							      "",
-							      NULL,
-							      G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_HEADER_BACKGROUND,
-					 g_param_spec_string ("header-background",
-							      "",
-							      "",
-							      NULL,
-							      G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_HEADER_LINE_BACKGROUND,
-					 g_param_spec_string ("header-line-background",
-							      "",
-							      "",
-							      NULL,
-							      G_PARAM_READWRITE));
-
-
-	g_object_class_install_property (object_class,
-					 PROP_TEXT_FOREGROUND,
-					 g_param_spec_string ("text-foreground",
-							      "",
-							      "",
-							      NULL,
-							      G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_TEXT_BACKGROUND,
-					 g_param_spec_string ("text-background",
-							      "",
-							      "",
-							      NULL,
-							      G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_ACTION_FOREGROUND,
-					 g_param_spec_string ("action-foreground",
-							      "",
-							      "",
-							      NULL,
-							      G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_HIGHLIGHT_FOREGROUND,
-					 g_param_spec_string ("highlight-foreground",
-							      "",
-							      "",
-							      NULL,
-							      G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_TIME_FOREGROUND,
-					 g_param_spec_string ("time-foreground",
-							      "",
-							      "",
-							      NULL,
-							      G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_EVENT_FOREGROUND,
-					 g_param_spec_string ("event-foreground",
-							      "",
-							      "",
-							      NULL,
-							      G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_INVITE_FOREGROUND,
-					 g_param_spec_string ("invite-foreground",
-							      "",
-							      "",
-							      NULL,
-							      G_PARAM_READWRITE));
-
-	g_object_class_install_property (object_class,
-					 PROP_LINK_FOREGROUND,
-					 g_param_spec_string ("link-foreground",
-							      "",
-							      "",
-							      NULL,
-							      G_PARAM_READWRITE));
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (theme));
 
-	g_type_class_add_private (object_class, sizeof (EmpathyThemeBoxesPriv));
-}
+	gtk_text_buffer_create_tag (buffer, EMPATHY_THEME_BOXES_TAG_HEADER,
+				    "pixels-above-lines", HEADER_PADDING,
+				    "pixels-below-lines", HEADER_PADDING,
+				    NULL);
 
-static void
-empathy_theme_boxes_init (EmpathyThemeBoxes *theme)
-{
-	EmpathyThemeBoxesPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (theme,
-		EMPATHY_TYPE_THEME_BOXES, EmpathyThemeBoxesPriv);
-
-	theme->priv = priv;
+	gtk_text_buffer_create_tag (buffer, EMPATHY_THEME_BOXES_TAG_HEADER_LINE, NULL);
 }
 
-static void
-theme_boxes_finalize (GObject *object)
+/* Pads a pixbuf to the specified size, by centering it in a larger transparent
+ * pixbuf. Returns a new ref.
+ */
+static GdkPixbuf *
+theme_boxes_pad_to_size (GdkPixbuf *pixbuf,
+			 gint       width,
+			 gint       height,
+			 gint       extra_padding_right)
 {
-	EmpathyThemeBoxesPriv *priv;
-
-	priv = GET_PRIV (object);
-
-	g_free (priv->header_foreground);
-	g_free (priv->header_background);
-	g_free (priv->header_line_background);
-	g_free (priv->text_foreground);
-	g_free (priv->text_background);
-	g_free (priv->action_foreground);
-	g_free (priv->highlight_foreground);
-	g_free (priv->time_foreground);
-	g_free (priv->event_foreground);
-	g_free (priv->invite_foreground);
-	g_free (priv->link_foreground);
-	
-	(G_OBJECT_CLASS (empathy_theme_boxes_parent_class)->finalize) (object);
+	gint       src_width, src_height;
+	GdkPixbuf *padded;
+	gint       x_offset, y_offset;
+
+	src_width = gdk_pixbuf_get_width (pixbuf);
+	src_height = gdk_pixbuf_get_height (pixbuf);
+
+	x_offset = (width - src_width) / 2;
+	y_offset = (height - src_height) / 2;
+
+	padded = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (pixbuf),
+				 TRUE, /* alpha */
+				 gdk_pixbuf_get_bits_per_sample (pixbuf),
+				 width + extra_padding_right,
+				 height);
+
+	gdk_pixbuf_fill (padded, 0);
+
+	gdk_pixbuf_copy_area (pixbuf,
+			      0, /* source coords */
+			      0,
+			      src_width,
+			      src_height,
+			      padded,
+			      x_offset, /* dest coords */
+			      y_offset);
+
+	return padded;
 }
 
-static void
-theme_boxes_get_property (GObject    *object,
-			  guint       param_id,
-			  GValue     *value,
-			  GParamSpec *pspec)
-{
-	EmpathyThemeBoxesPriv *priv;
-
-	priv = GET_PRIV (object);
-
-	switch (param_id) {
-	case PROP_HEADER_FOREGROUND:
-		g_value_set_string (value, priv->header_foreground);
-		break;
-	case PROP_HEADER_BACKGROUND:
-		g_value_set_string (value, priv->header_background);
-		break;
-	case PROP_HEADER_LINE_BACKGROUND:
-		g_value_set_string (value, priv->header_line_background);
-		break;
-	case PROP_TEXT_FOREGROUND:
-		g_value_set_string (value, priv->text_foreground);
-		break;
-	case PROP_TEXT_BACKGROUND:
-		g_value_set_string (value, priv->text_background);
-		break;
-	case PROP_ACTION_FOREGROUND:
-		g_value_set_string (value, priv->action_foreground);
-		break;
-	case PROP_HIGHLIGHT_FOREGROUND:
-		g_value_set_string (value, priv->highlight_foreground);
-		break;
-	case PROP_TIME_FOREGROUND:
-		g_value_set_string (value, priv->time_foreground);
-		break;
-	case PROP_EVENT_FOREGROUND:
-		g_value_set_string (value, priv->event_foreground);
-		break;
-	case PROP_INVITE_FOREGROUND:
-		g_value_set_string (value, priv->invite_foreground);
-		break;
-	case PROP_LINK_FOREGROUND:
-		g_value_set_string (value, priv->link_foreground);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
-		break;
-	}
-}
-static void
-theme_boxes_set_property (GObject      *object,
-			  guint         param_id,
-			  const GValue *value,
-			  GParamSpec   *pspec)
-{
-	EmpathyThemeBoxesPriv *priv;
-
-	priv = GET_PRIV (object);
-
-	switch (param_id) {
-	case PROP_HEADER_FOREGROUND:
-		g_free (priv->header_foreground);
-		priv->header_foreground = g_value_dup_string (value);
-		g_object_notify (object, "header-foreground");
-		break;
-	case PROP_HEADER_BACKGROUND:
-		g_free (priv->header_background);
-		priv->header_background = g_value_dup_string (value);
-		g_object_notify (object, "header-background");
-		break;
-	case PROP_HEADER_LINE_BACKGROUND:
-		g_free (priv->header_line_background);
-		priv->header_line_background = g_value_dup_string (value);
-		g_object_notify (object, "header-line_background");
-		break;
-	case PROP_TEXT_FOREGROUND:
-		g_free (priv->text_foreground);
-		priv->text_foreground = g_value_dup_string (value);
-		g_object_notify (object, "text-foreground");
-		break;
-	case PROP_TEXT_BACKGROUND:
-		g_free (priv->text_background);
-		priv->text_background = g_value_dup_string (value);
-		g_object_notify (object, "text-background");
-		break;
-	case PROP_ACTION_FOREGROUND:
-		g_free (priv->action_foreground);
-		priv->action_foreground = g_value_dup_string (value);
-		g_object_notify (object, "action-foreground");
-		break;
-	case PROP_HIGHLIGHT_FOREGROUND:
-		g_free (priv->highlight_foreground);
-		priv->highlight_foreground = g_value_dup_string (value);
-		g_object_notify (object, "highlight-foreground");
-		break;
-	case PROP_TIME_FOREGROUND:
-		g_free (priv->time_foreground);
-		priv->time_foreground = g_value_dup_string (value);
-		g_object_notify (object, "time-foreground");
-		break;
-	case PROP_EVENT_FOREGROUND:
-		g_free (priv->event_foreground);
-		priv->event_foreground = g_value_dup_string (value);
-		g_object_notify (object, "event-foreground");
-		break;
-	case PROP_INVITE_FOREGROUND:
-		g_free (priv->invite_foreground);
-		priv->invite_foreground = g_value_dup_string (value);
-		g_object_notify (object, "invite-foreground");
-		break;
-	case PROP_LINK_FOREGROUND:
-		g_free (priv->link_foreground);
-		priv->link_foreground = g_value_dup_string (value);
-		g_object_notify (object, "link-foreground");
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
-		break;
-	}
-}
+typedef struct {
+	GdkPixbuf *pixbuf;
+	gchar     *token;
+} AvatarData;
 
 static void
-theme_boxes_define_theme_tags (EmpathyTheme *theme, EmpathyChatView *view)
+theme_boxes_avatar_cache_data_free (gpointer ptr)
 {
-	EmpathyThemeBoxesPriv *priv;
-	GtkTextBuffer         *buffer;
-	GtkTextTag            *tag;
-
-	priv = GET_PRIV (theme);
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-
-	empathy_text_buffer_tag_set (buffer, "fancy-spacing",
-				     "size", 3000,
-				     "pixels-above-lines", 8,
-				     NULL);
-
-	tag = empathy_text_buffer_tag_set (buffer, "fancy-header",
-					   "weight", PANGO_WEIGHT_BOLD,
-					   "pixels-above-lines", HEADER_PADDING,
-					   "pixels-below-lines", HEADER_PADDING,
-					   NULL);
-	if (priv->header_foreground) {
-		g_object_set (tag,
-			      "foreground", priv->header_foreground,
-			      "paragraph-background", priv->header_background,
-			      NULL);
-	}
-
-	tag = empathy_text_buffer_tag_set (buffer, "fancy-header-line",
-					   "size", 1,
-					   NULL);
-	if (priv->header_line_background) {
-		g_object_set (tag,
-			      "paragraph-background", priv->header_line_background,
-			      NULL);
-	}
+	AvatarData *data = ptr;
 
-	tag = empathy_text_buffer_tag_set (buffer, "fancy-body",
-					   "pixels-above-lines", 4,
-					   NULL);
-	if (priv->text_background) {
-		g_object_set (tag,
-			      "paragraph-background", priv->text_background,
-			      NULL);
-	}
-
-	if (priv->text_foreground) {
-		g_object_set (tag,
-			      "foreground", priv->text_foreground,
-			      NULL);
-	}
-
-	tag = empathy_text_buffer_tag_set (buffer, "fancy-action",
-					   "style", PANGO_STYLE_ITALIC,
-					   "pixels-above-lines", 4,
-					   NULL);
-
-	if (priv->text_background) {
-		g_object_set (tag,
-			      "paragraph-background", priv->text_background,
-			      NULL);
-	}
-
-	if (priv->action_foreground) {
-		g_object_set (tag,
-			      "foreground", priv->action_foreground,
-			      NULL);
-	}
-
-	tag = empathy_text_buffer_tag_set (buffer, "fancy-highlight",
-					   "weight", PANGO_WEIGHT_BOLD,
-					   "pixels-above-lines", 4,
-					   NULL);
-	if (priv->text_background) {
-		g_object_set (tag,
-			      "paragraph-background", priv->text_background,
-			      NULL);
-	}
-
-
-	if (priv->highlight_foreground) {
-		g_object_set (tag,
-			      "foreground", priv->highlight_foreground,
-			      NULL);
-	}
+	g_object_unref (data->pixbuf);
+	g_free (data->token);
+	g_slice_free (AvatarData, data);
+}
 
-	tag = empathy_text_buffer_tag_set (buffer, "fancy-time",
-					   "justification", GTK_JUSTIFY_CENTER,
-					   NULL);
-	if (priv->time_foreground) {
-		g_object_set (tag,
-			      "foreground", priv->time_foreground,
-			      NULL);
+static GdkPixbuf *
+theme_boxes_get_avatar_pixbuf_with_cache (EmpathyContact *contact)
+{
+	AvatarData        *data;
+	EmpathyAvatar     *avatar;
+	GdkPixbuf         *tmp_pixbuf;
+	GdkPixbuf         *pixbuf = NULL;
+
+	/* Check if avatar is in cache and if it's up to date */
+	avatar = empathy_contact_get_avatar (contact);
+	data = g_object_get_data (G_OBJECT (contact), "chat-view-avatar-cache");
+	if (data) {
+		if (avatar && !tp_strdiff (avatar->token, data->token)) {
+			/* We have the avatar in cache */
+			return data->pixbuf;
+		}
 	}
 
-	tag = empathy_text_buffer_tag_set (buffer, "fancy-event",
-					   "justification", GTK_JUSTIFY_LEFT,
-					   NULL);
-	if (priv->event_foreground) {
-		g_object_set (tag,
-			      "foreground", priv->event_foreground,
-			      NULL);
+	/* Avatar not in cache, create pixbuf */
+	tmp_pixbuf = empathy_pixbuf_avatar_from_contact_scaled (contact, 32, 32);
+	if (tmp_pixbuf) {
+		pixbuf = theme_boxes_pad_to_size (tmp_pixbuf, 32, 32, 6);
+		g_object_unref (tmp_pixbuf);
 	}
-
-	tag = empathy_text_buffer_tag_set (buffer, "invite", NULL);
-	if (priv->invite_foreground) {
-		g_object_set (tag,
-			      "foreground", priv->invite_foreground,
-			      NULL);
+	if (!pixbuf) {
+		return NULL;
 	}
 
-	tag = empathy_text_buffer_tag_set (buffer, "fancy-link",
-					   "underline", PANGO_UNDERLINE_SINGLE,
-					   NULL);
-	if (priv->link_foreground) {
-		g_object_set (tag,
-			      "foreground", priv->link_foreground,
-			      NULL);
-	} 
-}
-
-static void
-theme_boxes_update_view (EmpathyTheme *theme, EmpathyChatView *view)
-{
-	EmpathyThemeBoxesPriv *priv;
+	/* Insert new pixbuf in cache */
+	data = g_slice_new0 (AvatarData);
+	data->token = g_strdup (avatar->token);
+	data->pixbuf = pixbuf;
 
-	g_return_if_fail (EMPATHY_IS_THEME_BOXES (theme));
-	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
+	g_object_set_data_full (G_OBJECT (contact), "chat-view-avatar-cache",
+				data, theme_boxes_avatar_cache_data_free);
 
-	priv = GET_PRIV (theme);
-
-	theme_boxes_define_theme_tags (theme, view);
-	
-	empathy_chat_view_set_margin (view, MARGIN);
+	return data->pixbuf;
 }
 
 static void
@@ -519,13 +178,13 @@ table_size_allocate_cb (GtkWidget     *view,
 }
 
 static void
-theme_boxes_maybe_append_header (EmpathyTheme        *theme,
-				 EmpathyChatView     *view,
-				 EmpathyMessage      *msg)
+theme_boxes_maybe_append_header (EmpathyThemeBoxes *theme,
+				 EmpathyMessage    *msg)
 {
-	EmpathyThemeBoxesPriv *priv;
-	EmpathyContact        *contact;
-	EmpathyContact        *last_contact;
+	EmpathyChatTextView  *view = EMPATHY_CHAT_TEXT_VIEW (theme);
+	EmpathyThemeBoxesPriv*priv = GET_PRIV (theme);
+	EmpathyContact       *contact;
+	EmpathyContact       *last_contact;
 	GdkPixbuf            *avatar = NULL;
 	GtkTextBuffer        *buffer;
 	const gchar          *name;
@@ -537,15 +196,14 @@ theme_boxes_maybe_append_header (EmpathyTheme        *theme,
 	time_t                time;
 	gchar                *tmp;
 	GtkTextIter           start;
-	GdkColor              color;
-	gboolean              parse_success;
-
-	priv = GET_PRIV (theme);
+	gboolean              color_set;
+	GtkTextTagTable      *table;
+	GtkTextTag           *tag;
 
 	contact = empathy_message_get_sender (msg);
 	name = empathy_contact_get_name (contact);
-	last_contact = empathy_chat_view_get_last_contact (view);
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+	last_contact = empathy_chat_text_view_get_last_contact (view);
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (theme));
 
 	DEBUG ("Maybe add fancy header");
 
@@ -556,24 +214,30 @@ theme_boxes_maybe_append_header (EmpathyTheme        *theme,
 		return;
 	}
 
-	empathy_theme_append_spacing (theme, view);
+	empathy_chat_text_view_append_spacing (view);
 
+	/* Insert header line */
 	gtk_text_buffer_get_end_iter (buffer, &iter);
 	gtk_text_buffer_insert_with_tags_by_name (buffer,
 						  &iter,
 						  "\n",
 						  -1,
-						  "fancy-header-line",
+						  EMPATHY_THEME_BOXES_TAG_HEADER_LINE,
 						  NULL);
 
 	gtk_text_buffer_get_end_iter (buffer, &iter);
 	anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
 
+	/* Create a hbox for the header and resize it when the view allocation
+	 * changes */
 	box = gtk_hbox_new (FALSE, 0);
+	g_signal_connect_object (view, "size-allocate",
+				 G_CALLBACK (table_size_allocate_cb),
+				 box, 0);
 
-
-	if (empathy_theme_get_show_avatars (theme)) {
-		avatar = empathy_chat_view_get_avatar_pixbuf_with_cache (contact);
+	/* Add avatar to the box if needed */
+	if (priv->show_avatars) {
+		avatar = theme_boxes_get_avatar_pixbuf_with_cache (contact);
 		if (avatar) {
 			GtkWidget *image;
 
@@ -584,218 +248,167 @@ theme_boxes_maybe_append_header (EmpathyTheme        *theme,
 		}
 	}
 
-	g_signal_connect_object (view, "size-allocate",
-				 G_CALLBACK (table_size_allocate_cb),
-				 box, 0);
-
+	/* Add contact alias */
 	str = g_markup_printf_escaped ("<b>%s</b>", name);
-
 	label1 = g_object_new (GTK_TYPE_LABEL,
 			       "label", str,
 			       "use-markup", TRUE,
 			       "xalign", 0.0,
 			       NULL);
-
-	parse_success = priv->header_foreground &&
-			gdk_color_parse (priv->header_foreground, &color);
-
-	if (parse_success) {
-		gtk_widget_modify_fg (label1, GTK_STATE_NORMAL, &color);
-	}
-
 	g_free (str);
 
+	/* Add the message receive time */
 	time = empathy_message_get_timestamp (msg);
-
 	tmp = empathy_time_to_string_local (time, 
 					   EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
 	str = g_strdup_printf ("<i>%s</i>", tmp);
-	g_free (tmp);
-
 	label2 = g_object_new (GTK_TYPE_LABEL,
 			       "label", str,
 			       "use-markup", TRUE,
 			       "xalign", 1.0,
 			       NULL);
+	g_free (tmp);
+	g_free (str);
 	
-	if (parse_success) {
+	/* Set foreground color of labels to the same color than the header tag. */
+	table = gtk_text_buffer_get_tag_table (buffer);
+	tag = gtk_text_tag_table_lookup (table, EMPATHY_THEME_BOXES_TAG_HEADER);
+	g_object_get (tag, "foreground-set", &color_set, NULL);
+	if (color_set) {
+		GdkColor color;
+		g_object_get (tag, "foreground-gdk", &color, NULL);
+		gtk_widget_modify_fg (label1, GTK_STATE_NORMAL, &color);
 		gtk_widget_modify_fg (label2, GTK_STATE_NORMAL, &color);
 	}
 
-	g_free (str);
-
+	/* Pack labels into the box */
 	gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.5);
 	gtk_misc_set_alignment (GTK_MISC (label2), 1.0, 0.5);
-
 	gtk_box_pack_start (GTK_BOX (box), label1, TRUE, TRUE, 0);
 	gtk_box_pack_start (GTK_BOX (box), label2, TRUE, TRUE, 0);
 
+	/* Add the header box to the text view */
 	gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view),
 					   box,
 					   anchor);
-
 	gtk_widget_show_all (box);
 
+	/* Insert a header line */
 	gtk_text_buffer_get_end_iter (buffer, &iter);
 	start = iter;
 	gtk_text_iter_backward_char (&start);
 	gtk_text_buffer_apply_tag_by_name (buffer,
-					   "fancy-header",
+					   EMPATHY_THEME_BOXES_TAG_HEADER,
 					   &start, &iter);
-
 	gtk_text_buffer_insert_with_tags_by_name (buffer,
 						  &iter,
 						  "\n",
 						  -1,
-						  "fancy-header",
+						  EMPATHY_THEME_BOXES_TAG_HEADER,
 						  NULL);
-
 	gtk_text_buffer_get_end_iter (buffer, &iter);
 	gtk_text_buffer_insert_with_tags_by_name (buffer,
 						  &iter,
 						  "\n",
 						  -1,
-						  "fancy-header-line",
+						  EMPATHY_THEME_BOXES_TAG_HEADER_LINE,
 						  NULL);
 }
 
 static void
-theme_boxes_append_message (EmpathyTheme        *theme,
-			    EmpathyChatView     *view,
+theme_boxes_append_message (EmpathyChatTextView *view,
 			    EmpathyMessage      *message)
 {
 	EmpathyContact *sender;
 
-	empathy_theme_maybe_append_date_and_time (theme, view, message);
-	theme_boxes_maybe_append_header (theme, view, message);
+	theme_boxes_maybe_append_header (EMPATHY_THEME_BOXES (view), message);
 
 	sender = empathy_message_get_sender (message);
-
-	if (empathy_message_get_tptype (message) == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) {
+	if (empathy_message_get_tptype (message) ==
+	    TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) {
 		gchar *body;
 
 		body = g_strdup_printf (" * %s %s", 
 					empathy_contact_get_name (sender),
 					empathy_message_get_body (message));
-		empathy_theme_append_text (theme, view, body,
-					   "fancy-action", "fancy-link");
+		empathy_chat_text_view_append_body (EMPATHY_CHAT_TEXT_VIEW (view),
+						    body,
+						    EMPATHY_CHAT_TEXT_VIEW_TAG_ACTION);
 	} else {
-		empathy_theme_append_text (theme, view,
-					   empathy_message_get_body (message),
-					   "fancy-body", "fancy-link");
+		empathy_chat_text_view_append_body (EMPATHY_CHAT_TEXT_VIEW (view),
+						    empathy_message_get_body (message),
+						    EMPATHY_CHAT_TEXT_VIEW_TAG_BODY);
 	}
 }
 
 static void
-theme_boxes_append_event (EmpathyTheme        *theme,
-			  EmpathyChatView     *view,
-			  const gchar        *str)
+theme_boxes_notify_show_avatars_cb (EmpathyConf *conf,
+				    const gchar *key,
+				    gpointer     user_data)
 {
-	GtkTextBuffer *buffer;
-	GtkTextIter    iter;
-	gchar         *msg;
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-
-	empathy_theme_maybe_append_date_and_time (theme, view, NULL);
-
-	gtk_text_buffer_get_end_iter (buffer, &iter);
-
-	msg = g_strdup_printf (" - %s\n", str);
-
-	gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
-						  msg, -1,
-						  "fancy-event",
-						  NULL);
-	g_free (msg);
+	EmpathyThemeBoxesPriv *priv = GET_PRIV (user_data);
+	
+	empathy_conf_get_bool (conf, key, &priv->show_avatars);
 }
 
 static void
-theme_boxes_append_timestamp (EmpathyTheme        *theme,
-			      EmpathyChatView     *view,
-			      EmpathyMessage      *message,
-			      gboolean            show_date,
-			      gboolean            show_time)
+theme_boxes_finalize (GObject *object)
 {
-	GtkTextBuffer *buffer;
-	time_t         timestamp;
-	GDate         *date;
-	GtkTextIter    iter;
-	GString       *str;
-	
-	if (!show_date) {
-		return;
-	}
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+	EmpathyThemeBoxesPriv *priv = GET_PRIV (object);
 
-	date = empathy_message_get_date_and_time (message, &timestamp);
+	empathy_conf_notify_remove (empathy_conf_get (),
+				    priv->notify_show_avatars_id);
 
-	str = g_string_new (NULL);
-
-	if (show_time || show_date) {
-		empathy_theme_append_spacing (theme, view);
-
-		g_string_append (str, "- ");
-	}
+	G_OBJECT_CLASS (empathy_theme_boxes_parent_class)->finalize (object);
+}
 
-	if (show_date) {
-		gchar buf[256];
+static void
+empathy_theme_boxes_class_init (EmpathyThemeBoxesClass *class)
+{
+	GObjectClass             *object_class;
+	EmpathyChatTextViewClass *chat_text_view_class;
 
-		g_date_strftime (buf, 256, _("%A %d %B %Y"), date);
-		g_string_append (str, buf);
+	object_class = G_OBJECT_CLASS (class);
+	chat_text_view_class  = EMPATHY_CHAT_TEXT_VIEW_CLASS (class);
 
-		if (show_time) {
-			g_string_append (str, ", ");
-		}
-	}
+	object_class->finalize = theme_boxes_finalize;
+	chat_text_view_class->append_message = theme_boxes_append_message;
 
-	g_date_free (date);
+	g_type_class_add_private (object_class, sizeof (EmpathyThemeBoxesPriv));
+}
 
-	if (show_time) {
-		gchar *tmp;
+static void
+empathy_theme_boxes_init (EmpathyThemeBoxes *theme)
+{
+	EmpathyThemeBoxesPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (theme,
+		EMPATHY_TYPE_THEME_BOXES, EmpathyThemeBoxesPriv);
 
-		tmp = empathy_time_to_string_local (timestamp, EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
-		g_string_append (str, tmp);
-		g_free (tmp);
-	}
+	theme->priv = priv;
 
-	if (show_time || show_date) {
-		g_string_append (str, " -\n");
+	theme_boxes_create_tags (theme);
 
-		gtk_text_buffer_get_end_iter (buffer, &iter);
-		gtk_text_buffer_insert_with_tags_by_name (buffer,
-							  &iter,
-							  str->str, -1,
-							  "fancy-time",
-							  NULL);
+	priv->notify_show_avatars_id =
+		empathy_conf_notify_add (empathy_conf_get (),
+					 EMPATHY_PREFS_UI_SHOW_AVATARS,
+					 theme_boxes_notify_show_avatars_cb,
+					 theme);
 
-		empathy_chat_view_set_last_timestamp (view, timestamp);
-	}
+	empathy_conf_get_bool (empathy_conf_get (),
+			       EMPATHY_PREFS_UI_SHOW_AVATARS,
+			       &priv->show_avatars);
 
-	g_string_free (str, TRUE);
-	
+	/* Define margin */
+	g_object_set (theme,
+		      "left-margin", MARGIN,
+		      "right-margin", MARGIN,
+		      NULL);
 }
 
-static void
-theme_boxes_append_spacing (EmpathyTheme        *theme,
-			    EmpathyChatView     *view)
+EmpathyThemeBoxes *
+empathy_theme_boxes_new (void)
 {
-	GtkTextBuffer *buffer;
-	GtkTextIter    iter;
-
-	g_return_if_fail (EMPATHY_IS_THEME (theme));
-	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-
-	gtk_text_buffer_get_end_iter (buffer, &iter);
-	gtk_text_buffer_insert_with_tags_by_name (buffer,
-						  &iter,
-						  "\n",
-						  -1,
-						  "cut",
-						  "fancy-spacing",
-						  NULL);
+	return g_object_new (EMPATHY_TYPE_THEME_BOXES,
+			     "only-if-date", TRUE,
+			     NULL);
 }
 
diff --git a/libempathy-gtk/empathy-theme-boxes.h b/libempathy-gtk/empathy-theme-boxes.h
index 5f6d15c..fed7cee 100644
--- a/libempathy-gtk/empathy-theme-boxes.h
+++ b/libempathy-gtk/empathy-theme-boxes.h
@@ -1,6 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2007 Imendio AB
+ * Copyright (C) 2008 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -16,6 +17,8 @@
  * License along with this program; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
  */
 
 #ifndef __EMPATHY_THEME_BOXES_H__
@@ -23,7 +26,7 @@
 
 #include <glib-object.h>
 
-#include "empathy-theme.h"
+#include "empathy-chat-text-view.h"
 
 G_BEGIN_DECLS
 
@@ -38,15 +41,19 @@ typedef struct _EmpathyThemeBoxes      EmpathyThemeBoxes;
 typedef struct _EmpathyThemeBoxesClass EmpathyThemeBoxesClass;
 
 struct _EmpathyThemeBoxes {
-	EmpathyTheme parent;
+	EmpathyChatTextView parent;
 	gpointer priv;
 };
 
 struct _EmpathyThemeBoxesClass {
-	EmpathyThemeClass parent_class;
+	EmpathyChatTextViewClass parent_class;
 };
 
-GType empathy_theme_boxes_get_type (void) G_GNUC_CONST;
+#define EMPATHY_THEME_BOXES_TAG_HEADER "fancy-header"
+#define EMPATHY_THEME_BOXES_TAG_HEADER_LINE "fancy-header-line"
+
+GType              empathy_theme_boxes_get_type (void) G_GNUC_CONST;
+EmpathyThemeBoxes *empathy_theme_boxes_new      (void);
 
 G_END_DECLS
 
diff --git a/libempathy-gtk/empathy-theme-irc.c b/libempathy-gtk/empathy-theme-irc.c
index fd315b2..3eefbc1 100644
--- a/libempathy-gtk/empathy-theme-irc.c
+++ b/libempathy-gtk/empathy-theme-irc.c
@@ -1,6 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2007 Imendio AB
+ * Copyright (C) 2008 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -16,6 +17,8 @@
  * License along with this program; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
  */
 
 #include "config.h"
@@ -23,201 +26,62 @@
 #include <glib/gi18n.h>
 
 #include <libempathy/empathy-utils.h>
-#include "empathy-chat.h"
-#include "empathy-ui-utils.h"
 #include "empathy-theme-irc.h"
+#include "empathy-ui-utils.h"
 
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeIrc)
 typedef struct {
-	gint my_prop;
+	gpointer dummy;
 } EmpathyThemeIrcPriv;
 
-static void         theme_irc_finalize         (GObject             *object);
-static void         theme_irc_update_view      (EmpathyTheme         *theme,
-						EmpathyChatView      *view);
-static void         theme_irc_append_message   (EmpathyTheme        *theme,
-						EmpathyChatView     *view,
-						EmpathyMessage      *message);
-static void         theme_irc_append_event     (EmpathyTheme        *theme,
-						EmpathyChatView     *view,
-						const gchar        *str);
-static void         theme_irc_append_timestamp (EmpathyTheme        *theme,
-						EmpathyChatView     *view,
-						EmpathyMessage      *message,
-						gboolean            show_date,
-						gboolean            show_time);
-static void         theme_irc_append_spacing   (EmpathyTheme        *theme,
-						EmpathyChatView     *view);
-
-
-enum {
-	PROP_0,
-	PROP_MY_PROP
-};
-
-G_DEFINE_TYPE (EmpathyThemeIrc, empathy_theme_irc, EMPATHY_TYPE_THEME);
-
-static void
-empathy_theme_irc_class_init (EmpathyThemeIrcClass *class)
-{
-	GObjectClass *object_class;
-	EmpathyThemeClass *theme_class;
-
-	object_class = G_OBJECT_CLASS (class);
-	theme_class  = EMPATHY_THEME_CLASS (class);
-
-	object_class->finalize     = theme_irc_finalize;
-
-	theme_class->update_view      = theme_irc_update_view;
-	theme_class->append_message   = theme_irc_append_message;
-	theme_class->append_event     = theme_irc_append_event;
-	theme_class->append_timestamp = theme_irc_append_timestamp;
-	theme_class->append_spacing   = theme_irc_append_spacing;
-
-	g_type_class_add_private (object_class, sizeof (EmpathyThemeIrcPriv));
-}
-
-static void
-empathy_theme_irc_init (EmpathyThemeIrc *theme)
-{
-	EmpathyThemeIrcPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (theme,
-		EMPATHY_TYPE_THEME_IRC, EmpathyThemeIrcPriv);
-
-	theme->priv = priv;
-}
-
-static void
-theme_irc_finalize (GObject *object)
-{
-	EmpathyThemeIrcPriv *priv;
-
-	priv = GET_PRIV (object);
-
-	(G_OBJECT_CLASS (empathy_theme_irc_parent_class)->finalize) (object);
-}
+G_DEFINE_TYPE (EmpathyThemeIrc, empathy_theme_irc, EMPATHY_TYPE_CHAT_TEXT_VIEW);
 
 static void
-theme_irc_apply_theme_classic (EmpathyTheme *theme, EmpathyChatView *view)
+theme_irc_create_tags (EmpathyThemeIrc *theme)
 {
-	EmpathyThemeIrcPriv *priv;
-	GtkTextBuffer       *buffer;
-
-	priv = GET_PRIV (theme);
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-
-	empathy_text_buffer_tag_set (buffer, "irc-spacing",
-				     "size", 2000,
-				     NULL);
-
-	empathy_text_buffer_tag_set (buffer, "irc-nick-self",
-				     "foreground", "sea green",
-				     NULL);
-
-	empathy_text_buffer_tag_set (buffer, "irc-body-self",
-				     /* To get the default theme color: */
-				     "foreground-set", FALSE,
-				     NULL);
-
-	empathy_text_buffer_tag_set (buffer, "irc-action-self",
-				     "foreground", "brown4",
-				     "style", PANGO_STYLE_ITALIC,
-				     NULL);
-
-	empathy_text_buffer_tag_set (buffer, "irc-nick-highlight",
-				     "foreground", "indian red",
-				     "weight", PANGO_WEIGHT_BOLD,
-				     NULL);
-
-	empathy_text_buffer_tag_set (buffer, "irc-nick-other",
-				     "foreground", "skyblue4",
-				     NULL);
-
-	empathy_text_buffer_tag_set (buffer, "irc-body-other",
-				     /* To get the default theme color: */
-				     "foreground-set", FALSE,
-				     NULL);
-
-	empathy_text_buffer_tag_set (buffer, "irc-action-other",
-				     "foreground", "brown4",
-				     "style", PANGO_STYLE_ITALIC,
-				     NULL);
-
-	empathy_text_buffer_tag_set (buffer, "irc-time",
-				     "foreground", "darkgrey",
-				     "justification", GTK_JUSTIFY_CENTER,
-				     NULL);
-
-	empathy_text_buffer_tag_set (buffer, "irc-event",
-				     "foreground", "PeachPuff4",
-				     "justification", GTK_JUSTIFY_LEFT,
-				     NULL);
-
-	empathy_text_buffer_tag_set (buffer, "invite",
-				     "foreground", "sienna",
-				     NULL);
-
-	empathy_text_buffer_tag_set (buffer, "irc-link",
-				     "foreground", "steelblue",
-				     "underline", PANGO_UNDERLINE_SINGLE,
-				     NULL);
-}
+	GtkTextBuffer *buffer;
 
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (theme));
 
-static void
-theme_irc_update_view (EmpathyTheme *theme, EmpathyChatView *view)
-{
-	theme_irc_apply_theme_classic (theme, view);
-	empathy_chat_view_set_margin (view, 3);
+	gtk_text_buffer_create_tag (buffer, EMPATHY_THEME_IRC_TAG_NICK_SELF, NULL);
+	gtk_text_buffer_create_tag (buffer, EMPATHY_THEME_IRC_TAG_NICK_OTHER, NULL);
+	gtk_text_buffer_create_tag (buffer, EMPATHY_THEME_IRC_TAG_NICK_HIGHLIGHT, NULL);
 }
 
 static void
-theme_irc_append_message (EmpathyTheme        *theme,
-			  EmpathyChatView     *view,
+theme_irc_append_message (EmpathyChatTextView *view,
 			  EmpathyMessage      *message)
 {
 	GtkTextBuffer *buffer;
 	const gchar   *name;
 	const gchar   *nick_tag;
-	const gchar   *body_tag;
 	GtkTextIter    iter;
 	gchar         *tmp;
 	EmpathyContact *contact;
 
-	empathy_theme_maybe_append_date_and_time (theme, view, message);
-
 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
 
 	contact = empathy_message_get_sender (message);
 	name = empathy_contact_get_name (contact);
 
 	if (empathy_message_get_tptype (message) == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) {
-		if (empathy_contact_is_user (contact)) {
-			body_tag = "irc-action-self";
-		} else {
-			body_tag = "irc-action-other";
-		}
-
 		tmp = g_strdup_printf (" * %s %s", 
 				       empathy_contact_get_name (contact),
 				       empathy_message_get_body (message));
-		empathy_theme_append_text (theme, view, tmp,
-					   body_tag, "irc-link");
+		empathy_chat_text_view_append_body (view, tmp,
+						    EMPATHY_CHAT_TEXT_VIEW_TAG_ACTION);
 		g_free (tmp);
 		return;
 	}
 
 	if (empathy_contact_is_user (contact)) {
-		nick_tag = "irc-nick-self";
-		body_tag = "irc-body-self";
+		nick_tag = EMPATHY_THEME_IRC_TAG_NICK_SELF;
 	} else {
 		if (empathy_message_should_highlight (message)) {
-			nick_tag = "irc-nick-highlight";
+			nick_tag = EMPATHY_THEME_IRC_TAG_NICK_HIGHLIGHT;
 		} else {
-			nick_tag = "irc-nick-other";
+			nick_tag = EMPATHY_THEME_IRC_TAG_NICK_OTHER;
 		}
-
-		body_tag = "irc-body-other";
 	}
 		
 	gtk_text_buffer_get_end_iter (buffer, &iter);
@@ -234,115 +98,52 @@ theme_irc_append_message (EmpathyTheme        *theme,
 	g_free (tmp);
 
 	/* The text body. */
-	empathy_theme_append_text (theme, view, 
-				  empathy_message_get_body (message),
-				  body_tag, "irc-link");
+	empathy_chat_text_view_append_body (view,
+					    empathy_message_get_body (message),
+					    EMPATHY_CHAT_TEXT_VIEW_TAG_BODY);
 }
 
 static void
-theme_irc_append_event (EmpathyTheme        *theme,
-		    EmpathyChatView     *view,
-		    const gchar        *str)
+theme_irc_finalize (GObject *object)
 {
-	GtkTextBuffer *buffer;
-	GtkTextIter    iter;
-	gchar         *msg;
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-	
-	empathy_theme_maybe_append_date_and_time (theme, view, NULL);
-
-	gtk_text_buffer_get_end_iter (buffer, &iter);
-
-	msg = g_strdup_printf (" - %s\n", str);
-	gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
-						  msg, -1,
-						  "irc-event",
-						  NULL);
-	g_free (msg);
+	(G_OBJECT_CLASS (empathy_theme_irc_parent_class)->finalize) (object);
 }
 
 static void
-theme_irc_append_timestamp (EmpathyTheme        *theme,
-			    EmpathyChatView     *view,
-			    EmpathyMessage      *message,
-			    gboolean            show_date,
-			    gboolean            show_time)
+empathy_theme_irc_class_init (EmpathyThemeIrcClass *class)
 {
-	GtkTextBuffer *buffer;
-	time_t         timestamp;
-	GDate         *date;
-	GtkTextIter    iter;
-	GString       *str;
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-
-	date = empathy_message_get_date_and_time (message, &timestamp);
-
-	str = g_string_new (NULL);
-
-	if (show_time || show_date) {
-		empathy_theme_append_spacing (theme, view);
-
-		g_string_append (str, "- ");
-	}
-
-	if (show_date) {
-		gchar buf[256];
-
-		g_date_strftime (buf, 256, _("%A %d %B %Y"), date);
-		g_string_append (str, buf);
-
-		if (show_time) {
-			g_string_append (str, ", ");
-		}
-	}
-
-	g_date_free (date);
-
-	if (show_time) {
-		gchar *tmp;
-
-		tmp = empathy_time_to_string_local (timestamp, EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
-		g_string_append (str, tmp);
-		g_free (tmp);
-	}
+	GObjectClass             *object_class;
+	EmpathyChatTextViewClass *chat_text_view_class;
 
-	if (show_time || show_date) {
-		g_string_append (str, " -\n");
-
-		gtk_text_buffer_get_end_iter (buffer, &iter);
-		gtk_text_buffer_insert_with_tags_by_name (buffer,
-							  &iter,
-							  str->str, -1,
-							  "irc-time",
-							  NULL);
+	object_class = G_OBJECT_CLASS (class);
+	chat_text_view_class = EMPATHY_CHAT_TEXT_VIEW_CLASS (class);
 
-		empathy_chat_view_set_last_timestamp (view, timestamp);
-	}
+	object_class->finalize = theme_irc_finalize;
+	chat_text_view_class->append_message = theme_irc_append_message;
 
-	g_string_free (str, TRUE);
+	g_type_class_add_private (object_class, sizeof (EmpathyThemeIrcPriv));
 }
 
 static void
-theme_irc_append_spacing (EmpathyTheme        *theme,
-			  EmpathyChatView     *view)
+empathy_theme_irc_init (EmpathyThemeIrc *theme)
 {
-	GtkTextBuffer *buffer;
-	GtkTextIter    iter;
+	EmpathyThemeIrcPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (theme,
+		EMPATHY_TYPE_THEME_IRC, EmpathyThemeIrcPriv);
 
-	g_return_if_fail (EMPATHY_IS_THEME (theme));
-	g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
+	theme->priv = priv;
 
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+	theme_irc_create_tags (theme);
 
-	gtk_text_buffer_get_end_iter (buffer, &iter);
-	gtk_text_buffer_insert_with_tags_by_name (buffer,
-						  &iter,
-						  "\n",
-						  -1,
-						  "cut",
-						  "irc-spacing",
-						  NULL);
+	/* Define margin */
+	g_object_set (theme,
+		      "left-margin", 3,
+		      "right-margin", 3,
+		      NULL);
+}
+
+EmpathyThemeIrc *
+empathy_theme_irc_new (void)
+{
+	return g_object_new (EMPATHY_TYPE_THEME_IRC, NULL);
 }
 
diff --git a/libempathy-gtk/empathy-theme-irc.h b/libempathy-gtk/empathy-theme-irc.h
index 0dbe155..58d82ac 100644
--- a/libempathy-gtk/empathy-theme-irc.h
+++ b/libempathy-gtk/empathy-theme-irc.h
@@ -1,6 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2007 Imendio AB
+ * Copyright (C) 2008 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -16,6 +17,8 @@
  * License along with this program; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
  */
 
 #ifndef __EMPATHY_THEME_IRC_H__
@@ -23,7 +26,7 @@
 
 #include <glib-object.h>
 
-#include "empathy-theme.h"
+#include "empathy-chat-text-view.h"
 
 G_BEGIN_DECLS
 
@@ -38,15 +41,20 @@ typedef struct _EmpathyThemeIrc      EmpathyThemeIrc;
 typedef struct _EmpathyThemeIrcClass EmpathyThemeIrcClass;
 
 struct _EmpathyThemeIrc {
-	EmpathyTheme parent;
+	EmpathyChatTextView parent;
 	gpointer priv;
 };
 
 struct _EmpathyThemeIrcClass {
-	EmpathyThemeClass parent_class;
+	EmpathyChatTextViewClass parent_class;
 };
 
-GType               empathy_theme_irc_get_type                 (void) G_GNUC_CONST;
+#define EMPATHY_THEME_IRC_TAG_NICK_SELF "irc-nick-self"
+#define EMPATHY_THEME_IRC_TAG_NICK_OTHER "irc-nick-other"
+#define EMPATHY_THEME_IRC_TAG_NICK_HIGHLIGHT "irc-nick-highlight"
+
+GType empathy_theme_irc_get_type (void) G_GNUC_CONST;
+EmpathyThemeIrc *empathy_theme_irc_new (void);
 
 G_END_DECLS
 
diff --git a/libempathy-gtk/empathy-theme-manager.c b/libempathy-gtk/empathy-theme-manager.c
index 1bcb420..d065a61 100644
--- a/libempathy-gtk/empathy-theme-manager.c
+++ b/libempathy-gtk/empathy-theme-manager.c
@@ -1,6 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2005-2007 Imendio AB
+ * Copyright (C) 2008 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -16,6 +17,8 @@
  * License along with this program; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
  */
 
 #include "config.h"
@@ -25,43 +28,33 @@
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
+#include <telepathy-glib/util.h>
 #include <libempathy/empathy-utils.h>
 
+#include "empathy-theme-manager.h"
 #include "empathy-chat-view.h"
 #include "empathy-conf.h"
-#include "empathy-theme.h"
+#include "empathy-chat-text-view.h"
 #include "empathy-theme-boxes.h"
 #include "empathy-theme-irc.h"
-#include "empathy-theme-manager.h"
+
+#ifdef HAVE_WEBKIT
+#include "empathy-theme-adium.h"
+#endif
+
+#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
+#include <libempathy/empathy-debug.h>
 
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeManager)
 typedef struct {
 	gchar       *name;
 	guint        name_notify_id;
-
-	gboolean     show_avatars;
-	guint        show_avatars_notify_id;
-
-	EmpathyTheme *clean_theme;
-	EmpathyTheme *simple_theme;
-	EmpathyTheme *blue_theme;
-	EmpathyTheme *classic_theme;
-
-	GtkSettings  *settings;
+	gchar       *adium_path;
+	guint        adium_path_notify_id;
+	GtkSettings *settings;
+	GList       *boxes_views;
 } EmpathyThemeManagerPriv;
 
-static void        theme_manager_finalize                 (GObject            *object);
-static void        theme_manager_notify_name_cb           (EmpathyConf         *conf,
-							   const gchar        *key,
-							   gpointer            user_data);
-
-static void        theme_manager_notify_show_avatars_cb   (EmpathyConf         *conf,
-							   const gchar        *key,
-							   gpointer            user_data);
-static void        theme_manager_apply_theme              (EmpathyThemeManager *manager,
-							   EmpathyChatView     *view,
-							   const gchar        *name);
-
 enum {
 	THEME_CHANGED,
 	LAST_SIGNAL
@@ -88,302 +81,447 @@ theme_manager_gdk_color_to_hex (GdkColor *gdk_color, gchar *str_color)
 		    gdk_color->green >> 8,
 		    gdk_color->blue >> 8);
 }
- 
- static void
-theme_manager_color_hash_notify_cb (EmpathyThemeManager *manager)
+
+static EmpathyThemeIrc *
+theme_manager_create_irc_view (EmpathyThemeManager *manager)
 {
-	EmpathyThemeManagerPriv *priv;
-	GtkStyle                *style;
-	gchar                    color[10];
+	EmpathyChatTextView *view;
+	EmpathyThemeIrc     *theme;
+
+	theme = empathy_theme_irc_new ();
+	view = EMPATHY_CHAT_TEXT_VIEW (theme);
+
+	/* Define base tags */
+	empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_SPACING,
+					"size", 2000,
+					NULL);
+	empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_TIME,
+					"foreground", "darkgrey",
+					"justification", GTK_JUSTIFY_CENTER,
+					NULL);
+	empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_ACTION,
+					"foreground", "brown4",
+					"style", PANGO_STYLE_ITALIC,
+					NULL);
+	empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_BODY,
+					"foreground-set", FALSE,
+					NULL);
+	empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_EVENT,
+					"foreground", "PeachPuff4",
+					"justification", GTK_JUSTIFY_LEFT,
+					NULL);
+	empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_LINK,
+					"foreground", "steelblue",
+					"underline", PANGO_UNDERLINE_SINGLE,
+					NULL);
+
+	/* Define IRC tags */
+	empathy_chat_text_view_tag_set (view, EMPATHY_THEME_IRC_TAG_NICK_SELF,
+					"foreground", "sea green",
+					NULL);
+	empathy_chat_text_view_tag_set (view, EMPATHY_THEME_IRC_TAG_NICK_OTHER,
+					"foreground", "skyblue4",
+					NULL);
+	empathy_chat_text_view_tag_set (view, EMPATHY_THEME_IRC_TAG_NICK_HIGHLIGHT,
+					"foreground", "indian red",
+					"weight", PANGO_WEIGHT_BOLD,
+					NULL);
+
+	return theme;
+}
 
-	priv = GET_PRIV (manager);
+static void
+theme_manager_boxes_weak_notify_cb (gpointer data,
+				    GObject *where_the_object_was)
+{
+	EmpathyThemeManagerPriv *priv = GET_PRIV (data);
 
-	style = gtk_widget_get_default_style ();
+	priv->boxes_views = g_list_remove (priv->boxes_views, where_the_object_was);
+}
 
-	g_object_freeze_notify (G_OBJECT (priv->simple_theme));
-
-	theme_manager_gdk_color_to_hex (&style->base[GTK_STATE_SELECTED], color);
-	g_object_set (priv->simple_theme,
-		      "action-foreground", color,
-		      "link-foreground", color,
-		      NULL);
- 
-	theme_manager_gdk_color_to_hex (&style->bg[GTK_STATE_SELECTED], color);
-	g_object_set (priv->simple_theme,
-		      "header-background", color,
-		      NULL);
-
-	theme_manager_gdk_color_to_hex (&style->dark[GTK_STATE_SELECTED], color);
-	g_object_set (priv->simple_theme,
-		      "header-line-background", color,
-		      NULL);
-
-	theme_manager_gdk_color_to_hex (&style->fg[GTK_STATE_SELECTED], color);
-	g_object_set (priv->simple_theme,
-		      "header-foreground", color,
-		      NULL);
-
-	g_object_thaw_notify (G_OBJECT (priv->simple_theme));
-
-#if 0
-
-FIXME: Make that work, it should update color when theme changes but it
-       doesnt seems to work with all themes.
-  
-	g_object_get (priv->settings,
-		      "color-hash", &color_hash,
-		      NULL);
-
-	/*
-	 * base_color: #ffffffffffff
-	 * fg_color: #000000000000
-	 * bg_color: #e6e6e7e7e8e8
-	 * text_color: #000000000000
-	 * selected_bg_color: #58589a9adbdb
-	 * selected_fg_color: #ffffffffffff
-	 */
-
-	color = g_hash_table_lookup (color_hash, "base_color");
-	if (color) {
-		theme_manager_gdk_color_to_hex (color, color_str);
-		g_object_set (priv->simple_theme,
-			      "action-foreground", color_str,
-			      "link-foreground", color_str,
-			      NULL);
-	}
+static EmpathyThemeBoxes *
+theme_manager_create_boxes_view (EmpathyThemeManager *manager)
+{
+	EmpathyThemeManagerPriv *priv = GET_PRIV (manager);
+	EmpathyThemeBoxes       *theme;
 
-	color = g_hash_table_lookup (color_hash, "selected_bg_color");
-	if (color) {
-		theme_manager_gdk_color_to_hex (color, color_str);
-		g_object_set (priv->simple_theme,
-			      "header-background", color_str,
-			      NULL);
-	}
+	theme = empathy_theme_boxes_new ();
+	priv->boxes_views = g_list_prepend (priv->boxes_views, theme);
+	g_object_weak_ref (G_OBJECT (theme),
+			   theme_manager_boxes_weak_notify_cb,
+			   manager);
 
-	color = g_hash_table_lookup (color_hash, "bg_color");
-	if (color) {
-		GdkColor tmp;
-
-		tmp = *color;
-		tmp.red /= 2;
-		tmp.green /= 2;
-		tmp.blue /= 2;
-		theme_manager_gdk_color_to_hex (&tmp, color_str);
-		g_object_set (priv->simple_theme,
-			      "header-line-background", color_str,
-			      NULL);
-	}
+	return theme;
+}
 
-	color = g_hash_table_lookup (color_hash, "selected_fg_color");
-	if (color) {
-		theme_manager_gdk_color_to_hex (color, color_str);
-		g_object_set (priv->simple_theme,
-			      "header-foreground", color_str,
-			      NULL);
-	}
+static void
+theme_manager_update_boxes_tags (EmpathyThemeBoxes *theme,
+				 const gchar       *header_foreground,
+				 const gchar       *header_background,
+				 const gchar       *header_line_background,
+				 const gchar       *action_foreground,
+				 const gchar       *time_foreground,
+				 const gchar       *event_foreground,
+				 const gchar       *link_foreground,
+				 const gchar       *text_foreground,
+				 const gchar       *text_background,
+				 const gchar       *highlight_foreground)
 
-	g_hash_table_unref (color_hash);
+{
+	EmpathyChatTextView *view = EMPATHY_CHAT_TEXT_VIEW (theme);
+	GtkTextTag          *tag;
+
+	DEBUG ("Update view with new colors:\n"
+		"header_foreground = %s\n"
+		"header_background = %s\n"
+		"header_line_background = %s\n"
+		"action_foreground = %s\n"
+		"time_foreground = %s\n"
+		"event_foreground = %s\n"
+		"link_foreground = %s\n"
+		"text_foreground = %s\n"
+		"text_background = %s\n"
+		"highlight_foreground = %s\n",
+		header_foreground, header_background, header_line_background,
+		action_foreground, time_foreground, event_foreground,
+		link_foreground, text_foreground, text_background,
+		highlight_foreground);
+
+
+	/* FIXME: GtkTextTag don't support to set color properties to NULL.
+	 * See bug #542523 */
+	
+	#define TAG_SET(prop, prop_set, value) \
+		if (value != NULL) { \
+			g_object_set (tag, prop, value, NULL); \
+		} else { \
+			g_object_set (tag, prop_set, FALSE, NULL); \
+		}
 
-#endif
+	/* Define base tags */
+	tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_HIGHLIGHT,
+					      "weight", PANGO_WEIGHT_BOLD,
+					      "pixels-above-lines", 4,
+					      NULL);
+	TAG_SET ("paragraph-background", "paragraph-background-set", text_background);
+	TAG_SET ("foreground", "foreground-set", highlight_foreground);
+
+	empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_SPACING,
+					"size", 3000,
+					"pixels-above-lines", 8,
+					NULL);
+	tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_TIME,
+					      "justification", GTK_JUSTIFY_CENTER,
+					      NULL);
+	TAG_SET ("foreground", "foreground-set", time_foreground);
+	tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_ACTION,
+					      "style", PANGO_STYLE_ITALIC,
+					      "pixels-above-lines", 4,
+					      NULL);
+	TAG_SET ("paragraph-background", "paragraph-background-set", text_background);
+	TAG_SET ("foreground", "foreground-set", action_foreground);
+	tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_BODY,
+					      "pixels-above-lines", 4,
+					      NULL);
+	TAG_SET ("paragraph-background", "paragraph-background-set", text_background);
+	TAG_SET ("foreground", "foreground-set", text_foreground);
+	tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_EVENT,
+					      "justification", GTK_JUSTIFY_LEFT,
+					      NULL);
+	TAG_SET ("foreground", "foreground-set", event_foreground);
+	tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_LINK,
+					      "underline", PANGO_UNDERLINE_SINGLE,
+					      NULL);
+	TAG_SET ("foreground", "foreground-set", link_foreground);
+
+	/* Define BOXES tags */
+	tag = empathy_chat_text_view_tag_set (view, EMPATHY_THEME_BOXES_TAG_HEADER,
+					      "weight", PANGO_WEIGHT_BOLD,
+					      "foreground", header_foreground,
+					      "paragraph-background", header_background,
+					      NULL);
+	TAG_SET ("foreground", "foreground-set", header_foreground);
+	TAG_SET ("paragraph-background", "paragraph-background-set", header_background);
+	tag = empathy_chat_text_view_tag_set (view, EMPATHY_THEME_BOXES_TAG_HEADER_LINE,
+					      "size", 1,
+					      "paragraph-background", header_line_background,
+					      NULL);
+	TAG_SET ("paragraph-background", "paragraph-background-set", header_line_background);
+
+	#undef TAG_SET
 }
 
 static void
-empathy_theme_manager_class_init (EmpathyThemeManagerClass *klass)
+theme_manager_update_simple_tags (EmpathyThemeBoxes *theme)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-	signals[THEME_CHANGED] =
-		g_signal_new ("theme-changed",
-			      G_OBJECT_CLASS_TYPE (object_class),
-			      G_SIGNAL_RUN_LAST,
-			      0,
-			      NULL, NULL,
-			      g_cclosure_marshal_VOID__VOID,
-			      G_TYPE_NONE,
-			      0);
+	GtkStyle *style;
+	gchar     color1[10];
+	gchar     color2[10];
+	gchar     color3[10];
+	gchar     color4[10];
 
-	g_type_class_add_private (object_class, sizeof (EmpathyThemeManagerPriv));
+	style = gtk_widget_get_default_style ();
 
-	object_class->finalize = theme_manager_finalize;
+	theme_manager_gdk_color_to_hex (&style->base[GTK_STATE_SELECTED], color1);
+	theme_manager_gdk_color_to_hex (&style->bg[GTK_STATE_SELECTED], color2);
+	theme_manager_gdk_color_to_hex (&style->dark[GTK_STATE_SELECTED], color3);
+	theme_manager_gdk_color_to_hex (&style->fg[GTK_STATE_SELECTED], color4);
+
+	theme_manager_update_boxes_tags (theme,
+					 color4,     /* header_foreground */
+					 color2,     /* header_background */
+					 color3,     /* header_line_background */
+					 color1,     /* action_foreground */
+					 "darkgrey", /* time_foreground */
+					 "darkgrey", /* event_foreground */
+					 color1,     /* link_foreground */
+					 NULL,       /* text_foreground */
+					 NULL,       /* text_background */
+					 NULL);      /* highlight_foreground */
 }
 
 static void
-empathy_theme_manager_init (EmpathyThemeManager *manager)
+theme_manager_update_boxes_theme (EmpathyThemeManager *manager,
+				  EmpathyThemeBoxes   *theme)
 {
-	EmpathyThemeManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
-		EMPATHY_TYPE_THEME_MANAGER, EmpathyThemeManagerPriv);
+	EmpathyThemeManagerPriv *priv = GET_PRIV (manager);
 
-	manager->priv = priv;
-	priv->name_notify_id =
-		empathy_conf_notify_add (empathy_conf_get (),
-					EMPATHY_PREFS_CHAT_THEME,
-					theme_manager_notify_name_cb,
-					manager);
+	if (strcmp (priv->name, "simple") == 0) {
+		theme_manager_update_simple_tags (theme);
+	}
+	else if (strcmp (priv->name, "clean") == 0) {
+		theme_manager_update_boxes_tags (theme,
+						 "black",    /* header_foreground */
+						 "#efefdf",  /* header_background */
+						 "#e3e3d3",  /* header_line_background */
+						 "brown4",   /* action_foreground */
+						 "darkgrey", /* time_foreground */
+						 "darkgrey", /* event_foreground */
+						 "#49789e",  /* link_foreground */
+						 NULL,       /* text_foreground */
+						 NULL,       /* text_background */
+						 NULL);      /* highlight_foreground */
+	}
+	else if (strcmp (priv->name, "blue") == 0) {
+		theme_manager_update_boxes_tags (theme,
+						 "black",    /* header_foreground */
+						 "#88a2b4",  /* header_background */
+						 "#7f96a4",  /* header_line_background */
+						 "brown4",   /* action_foreground */
+						 "darkgrey", /* time_foreground */
+						 "#7f96a4",  /* event_foreground */
+						 "#49789e",  /* link_foreground */
+						 "black",    /* text_foreground */
+						 "#adbdc8",  /* text_background */
+						 "black");   /* highlight_foreground */
+	}
+}
 
-	empathy_conf_get_string (empathy_conf_get (),
-				EMPATHY_PREFS_CHAT_THEME,
-				&priv->name);
+EmpathyChatView *
+empathy_theme_manager_create_view (EmpathyThemeManager *manager)
+{
+	EmpathyThemeManagerPriv *priv = GET_PRIV (manager);
+	EmpathyThemeBoxes       *theme;
 
-	/* Unused right now, but will be used soon. */
-	priv->show_avatars_notify_id =
-		empathy_conf_notify_add (empathy_conf_get (),
-					EMPATHY_PREFS_UI_SHOW_AVATARS,
-					theme_manager_notify_show_avatars_cb,
-					manager);
+	g_return_val_if_fail (EMPATHY_IS_THEME_MANAGER (manager), NULL);
 
-	empathy_conf_get_bool (empathy_conf_get (),
-			      EMPATHY_PREFS_UI_SHOW_AVATARS,
-			      &priv->show_avatars);
+	DEBUG ("Using theme %s", priv->name);
 
-	priv->settings = gtk_settings_get_default ();
-	g_signal_connect_swapped (priv->settings, "notify::color-hash",
-				  G_CALLBACK (theme_manager_color_hash_notify_cb),
-				  manager);
+#ifdef HAVE_WEBKIT
+	if (strcmp (priv->name, EMPATHY_THEME_ADIUM_NAME) == 0)  {
+		if (empathy_theme_adium_is_valid (priv->adium_path)) {
+			return EMPATHY_CHAT_VIEW (empathy_theme_adium_new (priv->adium_path));
+		} else {
+			/* The adium path is not valid, fallback to classic theme */
+			return EMPATHY_CHAT_VIEW (theme_manager_create_irc_view (manager));
+		}
+	}
+#endif
+
+	if (strcmp (priv->name, "classic") == 0)  {
+		return EMPATHY_CHAT_VIEW (theme_manager_create_irc_view (manager));
+	}
 
-	priv->simple_theme = g_object_new (EMPATHY_TYPE_THEME_BOXES, NULL);
-	theme_manager_color_hash_notify_cb (manager);
-
-	priv->clean_theme = g_object_new (EMPATHY_TYPE_THEME_BOXES,
-					  "header-foreground", "black",
-					  "header-background", "#efefdf",
-					  "header_line_background", "#e3e3d3",
-					  "action_foreground", "brown4",
-					  "time_foreground", "darkgrey",
-					  "event_foreground", "darkgrey",
-					  "invite_foreground", "sienna",
-					  "link_foreground","#49789e",
-					  NULL);
-
-	priv->blue_theme = g_object_new (EMPATHY_TYPE_THEME_BOXES,
-					 "header_foreground", "black",
-					 "header_background", "#88a2b4",
-					 "header_line_background", "#7f96a4",
-					 "text_foreground", "black",
-					 "text_background", "#adbdc8",
-					 "highlight_foreground", "black",
-					 "action_foreground", "brown4",
-					 "time_foreground", "darkgrey",
-					 "event_foreground", "#7f96a4",
-					 "invite_foreground", "sienna",
-					 "link_foreground", "#49789e",
-					 NULL);
-
-	priv->classic_theme = g_object_new (EMPATHY_TYPE_THEME_IRC, NULL);
+	theme = theme_manager_create_boxes_view (manager);
+	theme_manager_update_boxes_theme (manager, theme);
+
+	return EMPATHY_CHAT_VIEW (theme);
 }
 
 static void
-theme_manager_finalize (GObject *object)
+theme_manager_color_hash_notify_cb (EmpathyThemeManager *manager)
 {
-	EmpathyThemeManagerPriv *priv;
+	EmpathyThemeManagerPriv *priv = GET_PRIV (manager);
 
-	priv = GET_PRIV (object);
+	/* FIXME: Make that work, it should update color when theme changes but
+	 * it doesnt seems to work with all themes. */
 
-	empathy_conf_notify_remove (empathy_conf_get (), priv->name_notify_id);
-	empathy_conf_notify_remove (empathy_conf_get (), priv->show_avatars_notify_id);
+	if (strcmp (priv->name, "simple") == 0) {
+		GList *l;
 
-	g_free (priv->name);
+		/* We are using the simple theme which use the GTK theme color,
+		 * Update views to use the new color. */
+		for (l = priv->boxes_views; l; l = l->next) {
+			theme_manager_update_simple_tags (EMPATHY_THEME_BOXES (l->data));
+		}
+	}
+}
+
+static gboolean
+theme_manager_ensure_theme_exists (const gchar *name)
+{
+	gint i;
 
-	g_object_unref (priv->clean_theme);
-	g_object_unref (priv->simple_theme);
-	g_object_unref (priv->blue_theme);
-	g_object_unref (priv->classic_theme);
+	if (G_STR_EMPTY (name)) {
+		return FALSE;
+	}
 
-	G_OBJECT_CLASS (empathy_theme_manager_parent_class)->finalize (object);
+	if (strcmp (EMPATHY_THEME_ADIUM_NAME, name) == 0) {
+		return TRUE;
+	}
+
+	for (i = 0; themes[i]; i += 2) {
+		if (strcmp (themes[i], name) == 0) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
 }
 
 static void
-theme_manager_notify_name_cb (EmpathyConf  *conf,
+theme_manager_notify_name_cb (EmpathyConf *conf,
 			      const gchar *key,
 			      gpointer     user_data)
 {
-	EmpathyThemeManager     *manager;
-	EmpathyThemeManagerPriv *priv;
-	gchar                  *name;
+	EmpathyThemeManager     *manager = EMPATHY_THEME_MANAGER (user_data);
+	EmpathyThemeManagerPriv *priv = GET_PRIV (manager);
+	gchar                   *name = NULL;
 
-	manager = user_data;
-	priv = GET_PRIV (manager);
+	if (!empathy_conf_get_string (conf, key, &name) ||
+	    !theme_manager_ensure_theme_exists (name) ||
+	    !tp_strdiff (priv->name, name)) {
+		if (!priv->name) {
+			priv->name = g_strdup ("classic");
+		}
+
+		g_free (name);
+		return;
+	}
 
 	g_free (priv->name);
+	priv->name = name;
 
-	name = NULL;
-	if (!empathy_conf_get_string (conf, key, &name) ||
-	    name == NULL || name[0] == 0) {
-		priv->name = g_strdup ("classic");
-		g_free (name);
-	} else {
-		priv->name = name;
+	if (!tp_strdiff (priv->name, "simple") ||
+	    !tp_strdiff (priv->name, "clean") ||
+	    !tp_strdiff (priv->name, "blue")) {
+		GList *l;
+
+		/* The theme changes to a boxed one, we can update boxed views */
+		for (l = priv->boxes_views; l; l = l->next) {
+			theme_manager_update_boxes_theme (manager,
+							  EMPATHY_THEME_BOXES (l->data));
+		}
 	}
 
 	g_signal_emit (manager, signals[THEME_CHANGED], 0, NULL);
 }
 
 static void
-theme_manager_notify_show_avatars_cb (EmpathyConf  *conf,
-				      const gchar *key,
-				      gpointer     user_data)
+theme_manager_notify_adium_path_cb (EmpathyConf *conf,
+				    const gchar *key,
+				    gpointer     user_data)
 {
-	EmpathyThemeManager     *manager;
-	EmpathyThemeManagerPriv *priv;
-	gboolean                value;
+	EmpathyThemeManager     *manager = EMPATHY_THEME_MANAGER (user_data);
+	EmpathyThemeManagerPriv *priv = GET_PRIV (manager);
+	gchar                   *adium_path = NULL;
+
+	if (!empathy_conf_get_string (conf, key, &adium_path) ||
+	    !tp_strdiff (priv->adium_path, adium_path)) {
+		g_free (adium_path);
+		return;
+	}
 
-	manager = user_data;
-	priv = GET_PRIV (manager);
+	g_free (priv->adium_path);
+	priv->adium_path = adium_path;
 
-	if (!empathy_conf_get_bool (conf, key, &value)) {
-		priv->show_avatars = FALSE;
-	} else {
-		priv->show_avatars = value;
-	}
+	g_signal_emit (manager, signals[THEME_CHANGED], 0, NULL);
 }
 
-static gboolean
-theme_manager_ensure_theme_exists (const gchar *name)
+static void
+theme_manager_finalize (GObject *object)
 {
-	gint i;
+	EmpathyThemeManagerPriv *priv = GET_PRIV (object);
+	GList                   *l;
 
-	if (G_STR_EMPTY (name)) {
-		return FALSE;
-	}
+	empathy_conf_notify_remove (empathy_conf_get (), priv->name_notify_id);
+	g_free (priv->name);
+	empathy_conf_notify_remove (empathy_conf_get (), priv->adium_path_notify_id);
+	g_free (priv->adium_path);
 
-	for (i = 0; themes[i]; i += 2) {
-		if (strcmp (themes[i], name) == 0) {
-			return TRUE;
-		}
+	for (l = priv->boxes_views; l; l = l->next) {
+		g_object_weak_unref (G_OBJECT (l->data),
+				     theme_manager_boxes_weak_notify_cb,
+				     object);
 	}
+	g_list_free (priv->boxes_views);
 
-	return FALSE;
+	G_OBJECT_CLASS (empathy_theme_manager_parent_class)->finalize (object);
 }
 
 static void
-theme_manager_apply_theme (EmpathyThemeManager *manager,
-			   EmpathyChatView     *view,
-			   const gchar        *name)
+empathy_theme_manager_class_init (EmpathyThemeManagerClass *klass)
 {
-	EmpathyThemeManagerPriv *priv;
-	EmpathyTheme            *theme;
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	signals[THEME_CHANGED] =
+		g_signal_new ("theme-changed",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
 
-	priv = GET_PRIV (manager);
+	g_type_class_add_private (object_class, sizeof (EmpathyThemeManagerPriv));
 
-	/* Make sure all tags are present. Note: not useful now but when we have
-	 * user defined theme it will be.
-	 */
-	if (theme_manager_ensure_theme_exists (name)) {
-		if (strcmp (name, "clean") == 0) {
-			theme = priv->clean_theme;
-		}
-		else if (strcmp (name, "simple") == 0) {
-			theme = priv->simple_theme;
-		}
-		else if (strcmp (name, "blue") == 0) {
-			theme = priv->blue_theme;
-		} else {
-			theme = priv->classic_theme;
-		}
-	} else {
-		theme = priv->classic_theme;
-	}
+	object_class->finalize = theme_manager_finalize;
+}
 
-	empathy_chat_view_set_theme (view, theme);
+static void
+empathy_theme_manager_init (EmpathyThemeManager *manager)
+{
+	EmpathyThemeManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
+		EMPATHY_TYPE_THEME_MANAGER, EmpathyThemeManagerPriv);
+
+	manager->priv = priv;
+
+	/* Take the theme name and track changes */
+	priv->name_notify_id =
+		empathy_conf_notify_add (empathy_conf_get (),
+					 EMPATHY_PREFS_CHAT_THEME,
+					 theme_manager_notify_name_cb,
+					 manager);
+	theme_manager_notify_name_cb (empathy_conf_get (),
+				      EMPATHY_PREFS_CHAT_THEME,
+				      manager);
+
+	/* Take the adium path and track changes */
+	priv->adium_path_notify_id =
+		empathy_conf_notify_add (empathy_conf_get (),
+					 EMPATHY_PREFS_CHAT_ADIUM_PATH,
+					 theme_manager_notify_adium_path_cb,
+					 manager);
+	theme_manager_notify_adium_path_cb (empathy_conf_get (),
+					    EMPATHY_PREFS_CHAT_ADIUM_PATH,
+					    manager);
+
+	/* Track GTK color changes */
+	priv->settings = gtk_settings_get_default ();
+	g_signal_connect_swapped (priv->settings, "notify::color-hash",
+				  G_CALLBACK (theme_manager_color_hash_notify_cb),
+				  manager);
 }
 
 EmpathyThemeManager *
@@ -404,26 +542,3 @@ empathy_theme_manager_get_themes (void)
 	return themes;
 }
 
-void
-empathy_theme_manager_apply (EmpathyThemeManager *manager,
-			    EmpathyChatView     *view,
-			    const gchar        *name)
-{
-	EmpathyThemeManagerPriv *priv;
-
-	priv = GET_PRIV (manager);
-
-	theme_manager_apply_theme (manager, view, name);
-}
-
-void
-empathy_theme_manager_apply_saved (EmpathyThemeManager *manager,
-				  EmpathyChatView     *view)
-{
-	EmpathyThemeManagerPriv *priv;
-
-	priv = GET_PRIV (manager);
-
-	theme_manager_apply_theme (manager, view, priv->name);
-}
-
diff --git a/libempathy-gtk/empathy-theme-manager.h b/libempathy-gtk/empathy-theme-manager.h
index 5c0f579..25d9285 100644
--- a/libempathy-gtk/empathy-theme-manager.h
+++ b/libempathy-gtk/empathy-theme-manager.h
@@ -1,6 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2005-2007 Imendio AB
+ * Copyright (C) 2008 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -16,12 +17,15 @@
  * License along with this program; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
  */
 
 #ifndef __EMPATHY_THEME_MANAGER_H__
 #define __EMPATHY_THEME_MANAGER_H__
 
 #include <glib-object.h>
+#include "empathy-chat-view.h"
 
 G_BEGIN_DECLS
 
@@ -35,6 +39,8 @@ G_BEGIN_DECLS
 typedef struct _EmpathyThemeManager      EmpathyThemeManager;
 typedef struct _EmpathyThemeManagerClass EmpathyThemeManagerClass;
 
+#define EMPATHY_THEME_ADIUM_NAME "adium"
+
 struct _EmpathyThemeManager {
 	GObject parent;
 	gpointer priv;
@@ -44,14 +50,10 @@ struct _EmpathyThemeManagerClass {
 	GObjectClass parent_class;
 };
 
-GType               empathy_theme_manager_get_type            (void) G_GNUC_CONST;
-EmpathyThemeManager *empathy_theme_manager_get                 (void);
-const gchar **      empathy_theme_manager_get_themes          (void);
-void                empathy_theme_manager_apply               (EmpathyThemeManager *manager,
-							      EmpathyChatView     *view,
-							      const gchar        *theme);
-void                empathy_theme_manager_apply_saved         (EmpathyThemeManager *manager,
-							      EmpathyChatView     *view);
+GType                   empathy_theme_manager_get_type    (void) G_GNUC_CONST;
+EmpathyThemeManager *   empathy_theme_manager_get         (void);
+const gchar **          empathy_theme_manager_get_themes  (void);
+EmpathyChatView *       empathy_theme_manager_create_view (EmpathyThemeManager *manager);
 
 G_END_DECLS
 
diff --git a/libempathy-gtk/empathy-theme.h b/libempathy-gtk/empathy-theme.h
deleted file mode 100644
index f82c18a..0000000
--- a/libempathy-gtk/empathy-theme.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2007 Imendio AB
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __EMPATHY_THEME_H__
-#define __EMPATHY_THEME_H__
-
-#include <glib-object.h>
-#include <gtk/gtktextbuffer.h>
-
-G_BEGIN_DECLS
-
-#define EMPATHY_TYPE_THEME            (empathy_theme_get_type ())
-#define EMPATHY_THEME(o)              (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_THEME, EmpathyTheme))
-#define EMPATHY_THEME_CLASS(k)        (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_THEME, EmpathyThemeClass))
-#define EMPATHY_IS_THEME(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_THEME))
-#define EMPATHY_IS_THEME_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_THEME))
-#define EMPATHY_THEME_GET_CLASS(o)    (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_THEME, EmpathyThemeClass))
-
-typedef struct _EmpathyTheme      EmpathyTheme;
-typedef struct _EmpathyThemeClass EmpathyThemeClass;
-
-#include "empathy-chat-view.h"
-
-struct _EmpathyTheme {
-	GObject parent;
-	gpointer priv;
-};
-
-struct _EmpathyThemeClass {
-	GObjectClass parent_class;
-
-	/* <vtable> */
-	void (*update_view)         (EmpathyTheme    *theme,
-				     EmpathyChatView *view);
-	void (*append_message)      (EmpathyTheme    *theme,
-				     EmpathyChatView *view,
-				     EmpathyMessage  *message);
-	void (*append_event)        (EmpathyTheme    *theme,
-				     EmpathyChatView *view,
-				     const gchar     *str);
-	void (*append_timestamp)    (EmpathyTheme    *theme,
-				     EmpathyChatView *view,
-				     EmpathyMessage  *message,
-				     gboolean         show_date,
-				     gboolean         show_time);
-	void (*append_spacing)      (EmpathyTheme    *theme,
-				     EmpathyChatView *view);
-};
-
-GType          empathy_theme_get_type                   (void) G_GNUC_CONST;
-void           empathy_theme_update_view                (EmpathyTheme    *theme,
-							 EmpathyChatView *view);
-void           empathy_theme_append_message             (EmpathyTheme    *theme,
-							 EmpathyChatView *view,
-							 EmpathyMessage  *msg);
-void           empathy_theme_append_text                (EmpathyTheme    *theme,
-							 EmpathyChatView *view,
-							 const gchar     *body,
-							 const gchar     *tag, 
-							 const gchar     *link_tag);
-void           empathy_theme_append_spacing             (EmpathyTheme    *theme,
-							 EmpathyChatView *view);
-void           empathy_theme_append_event               (EmpathyTheme    *theme,
-							 EmpathyChatView *view,
-							 const gchar     *str);
-void           empathy_theme_append_timestamp           (EmpathyTheme    *theme,
-							 EmpathyChatView *view,
-							 EmpathyMessage  *message,
-							 gboolean         show_date,
-							 gboolean         show_time);
-void           empathy_theme_maybe_append_date_and_time (EmpathyTheme    *theme,
-							 EmpathyChatView *view,
-							 EmpathyMessage  *message);
-gboolean       empathy_theme_get_show_avatars           (EmpathyTheme    *theme);
-void           empathy_theme_set_show_avatars           (EmpathyTheme    *theme,
-							 gboolean         show);
-
-G_END_DECLS
-
-#endif /* __EMPATHY_THEME_H__ */
-
diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c
index ae36751..6ea2cd4 100644
--- a/libempathy-gtk/empathy-ui-utils.c
+++ b/libempathy-gtk/empathy-ui-utils.c
@@ -533,6 +533,29 @@ empathy_pixbuf_from_icon_name (const gchar *icon_name,
 	return pixbuf;
 }
 
+gchar *
+empathy_filename_from_icon_name (const gchar *icon_name,
+				 GtkIconSize  icon_size)
+{
+	GtkIconTheme *icon_theme;
+	GtkIconInfo  *icon_info;
+	gint          w, h;
+	gint          size = 48;
+	gchar        *ret;
+
+	icon_theme = gtk_icon_theme_get_default ();
+
+	if (gtk_icon_size_lookup (icon_size, &w, &h)) {
+		size = (w + h) / 2;
+	}
+
+	icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size, 0);
+	ret = g_strdup (gtk_icon_info_get_filename (icon_info));
+	gtk_icon_info_free (icon_info);
+
+	return ret;
+}
+
 /* Stolen from GtkSourceView, hence the weird intendation. Please keep it like
  * that to make it easier to apply changes from the original code.
  */
@@ -1377,51 +1400,3 @@ empathy_toggle_button_set_state_quietly (GtkWidget *widget,
 	g_signal_handlers_unblock_by_func (widget, callback, user_data);
 }
 
-GtkTextTag *
-empathy_text_buffer_tag_set (GtkTextBuffer *buffer,
-			     const gchar   *tag_name,
-			     const gchar   *first_property_name,
-			     ...)
-{
-	GtkTextTagTable *table;
-	GtkTextTag      *tag;
-
-	g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
-	g_return_val_if_fail (tag_name != NULL, NULL);
-
-	table = gtk_text_buffer_get_tag_table (buffer);
-	tag = gtk_text_tag_table_lookup (table, tag_name);
-
-	if (!tag) {
-		tag = gtk_text_tag_new (tag_name);
-		gtk_text_tag_table_add (table, tag);
-		g_object_unref (tag);
-	} else {
-		/* Clear the old values so that we don't affect the new theme. */
-		g_object_set (tag,
-			      "background-set", FALSE,
-			      "foreground-set", FALSE,
-			      "invisible-set", FALSE,
-			      "justification-set", FALSE,
-			      "paragraph-background-set", FALSE,
-			      "pixels-above-lines-set", FALSE,
-			      "pixels-below-lines-set", FALSE,
-			      "rise-set", FALSE,
-			      "scale-set", FALSE,
-			      "size-set", FALSE,
-			      "style-set", FALSE,
-			      "weight-set", FALSE,
-			      NULL);
-	}
-
-	if (first_property_name) {
-		va_list list;
-
-		va_start (list, first_property_name);
-		g_object_set_valist (G_OBJECT (tag), first_property_name, list);
-		va_end (list);
-	}
-
-	return tag;
-}
-
diff --git a/libempathy-gtk/empathy-ui-utils.h b/libempathy-gtk/empathy-ui-utils.h
index 24ab01f..8077952 100644
--- a/libempathy-gtk/empathy-ui-utils.h
+++ b/libempathy-gtk/empathy-ui-utils.h
@@ -76,10 +76,13 @@ GdkPixbuf *   empathy_pixbuf_from_avatar_scaled         (EmpathyAvatar    *avata
 GdkPixbuf *   empathy_pixbuf_avatar_from_contact_scaled (EmpathyContact   *contact,
 							 gint              width,
 							 gint              height);
-GdkPixbuf * empathy_pixbuf_scale_down_if_necessary      (GdkPixbuf        *pixbuf,
+GdkPixbuf *   empathy_pixbuf_scale_down_if_necessary    (GdkPixbuf        *pixbuf,
 							 gint              max_size);
-GdkPixbuf * empathy_pixbuf_from_icon_name               (const gchar      *icon_name,
+GdkPixbuf *   empathy_pixbuf_from_icon_name             (const gchar      *icon_name,
 							 GtkIconSize       icon_size);
+gchar *       empathy_filename_from_icon_name           (const gchar      *icon_name,
+							 GtkIconSize       icon_size);
+
 /* Text view */
 gboolean    empathy_text_iter_forward_search            (const GtkTextIter*iter,
 							 const gchar      *str,
@@ -105,10 +108,6 @@ void        empathy_toggle_button_set_state_quietly     (GtkWidget        *widge
 							 gboolean          active);
 GtkWidget * empathy_link_button_new                     (const gchar      *url,
 							 const gchar      *title);
-GtkTextTag *empathy_text_buffer_tag_set                 (GtkTextBuffer *buffer,
-							 const gchar   *tag_name,
-							 const gchar   *first_property_name,
-							 ...);
 
 G_END_DECLS
 
diff --git a/libempathy/empathy-contact.c b/libempathy/empathy-contact.c
index 6aed12a..e3a49a9 100644
--- a/libempathy/empathy-contact.c
+++ b/libempathy/empathy-contact.c
@@ -795,7 +795,7 @@ contact_get_avatar_filename (EmpathyContact *contact,
 
 void
 empathy_contact_load_avatar_data (EmpathyContact *contact,
-                                  const guchar  *data,
+                                  const guchar *data,
                                   const gsize len,
                                   const gchar *format,
                                   const gchar *token)
@@ -811,13 +811,13 @@ empathy_contact_load_avatar_data (EmpathyContact *contact,
   g_return_if_fail (!G_STR_EMPTY (token));
 
   /* Load and set the avatar */
+  filename = contact_get_avatar_filename (contact, token);
   avatar = empathy_avatar_new (g_memdup (data, len), len, g_strdup (format),
-      g_strdup (token));
+      g_strdup (token), filename);
   empathy_contact_set_avatar (contact, avatar);
   empathy_avatar_unref (avatar);
 
   /* Save to cache if not yet in it */
-  filename = contact_get_avatar_filename (contact, token);
   if (filename && !g_file_test (filename, G_FILE_TEST_EXISTS))
     {
       if (!empathy_avatar_save_to_file (avatar, filename, &error))
@@ -829,7 +829,6 @@ empathy_contact_load_avatar_data (EmpathyContact *contact,
       else
           DEBUG ("Avatar saved to %s", filename);
     }
-  g_free (filename);
 }
 
 gboolean
@@ -860,13 +859,11 @@ empathy_contact_load_avatar_cache (EmpathyContact *contact,
   if (data)
     {
       DEBUG ("Avatar loaded from %s", filename);
-      avatar = empathy_avatar_new (data, len, NULL, g_strdup (token));
+      avatar = empathy_avatar_new (data, len, NULL, g_strdup (token), filename);
       empathy_contact_set_avatar (contact, avatar);
       empathy_avatar_unref (avatar);
     }
 
-  g_free (filename);
-
   return data != NULL;
 }
 
@@ -889,7 +886,8 @@ EmpathyAvatar *
 empathy_avatar_new (guchar *data,
                     gsize len,
                     gchar *format,
-                    gchar *token)
+                    gchar *token,
+                    gchar *filename)
 {
   EmpathyAvatar *avatar;
 
@@ -898,6 +896,7 @@ empathy_avatar_new (guchar *data,
   avatar->len = len;
   avatar->format = format;
   avatar->token = token;
+  avatar->filename = filename;
   avatar->refcount = 1;
 
   return avatar;
diff --git a/libempathy/empathy-contact.h b/libempathy/empathy-contact.h
index da018fd..38808f6 100644
--- a/libempathy/empathy-contact.h
+++ b/libempathy/empathy-contact.h
@@ -59,6 +59,7 @@ typedef struct {
   gsize len;
   gchar *format;
   gchar *token;
+  gchar *filename;
   guint refcount;
 } EmpathyAvatar;
 
@@ -123,7 +124,8 @@ GType empathy_avatar_get_type (void) G_GNUC_CONST;
 EmpathyAvatar * empathy_avatar_new (guchar *data,
     gsize len,
     gchar *format,
-    gchar *token);
+    gchar *token,
+    gchar *filename);
 EmpathyAvatar * empathy_avatar_ref (EmpathyAvatar *avatar);
 void empathy_avatar_unref (EmpathyAvatar *avatar);
 
diff --git a/libempathy/empathy-message.c b/libempathy/empathy-message.c
index 6636fe6..c15fdc2 100644
--- a/libempathy/empathy-message.c
+++ b/libempathy/empathy-message.c
@@ -383,26 +383,6 @@ empathy_message_set_timestamp (EmpathyMessage *message,
 	g_object_notify (G_OBJECT (message), "timestamp");
 }
 
-GDate *
-empathy_message_get_date_and_time (EmpathyMessage *message, time_t *timestamp)
-{
-	GDate *date;
-
-	*timestamp = 0;
-	if (message) {
-		*timestamp = empathy_message_get_timestamp (message);
-	}
-
-	if (timestamp <= 0) {
-		*timestamp = empathy_time_get_current ();
-	}
-
-	date = g_date_new ();
-	g_date_set_time_t (date, *timestamp);
-
-	return date;
-}
-
 #define IS_SEPARATOR(ch) (ch == ' ' || ch == ',' || ch == '.' || ch == ':')
 gboolean
 empathy_message_should_highlight (EmpathyMessage *message)
diff --git a/libempathy/empathy-message.h b/libempathy/empathy-message.h
index fd3e073..b944280 100644
--- a/libempathy/empathy-message.h
+++ b/libempathy/empathy-message.h
@@ -68,8 +68,6 @@ void                     empathy_message_set_body          (EmpathyMessage
 time_t                   empathy_message_get_timestamp     (EmpathyMessage           *message);
 void                     empathy_message_set_timestamp     (EmpathyMessage           *message,
 							    time_t                    timestamp);
-GDate *                  empathy_message_get_date_and_time (EmpathyMessage           *message,
-							    time_t                   *timestamp);
 gboolean                 empathy_message_should_highlight  (EmpathyMessage           *message);
 TpChannelTextMessageType empathy_message_type_from_str     (const gchar              *type_str);
 const gchar *            empathy_message_type_to_str       (TpChannelTextMessageType  type);
diff --git a/python/pyempathy/pyempathy.defs b/python/pyempathy/pyempathy.defs
index da866fd..c90cc73 100644
--- a/python/pyempathy/pyempathy.defs
+++ b/python/pyempathy/pyempathy.defs
@@ -626,6 +626,7 @@
     '("gsize" "len")
     '("gchar*" "format")
     '("gchar*" "token")
+    '("gchar*" "filename")
   )
 )
 
@@ -1435,15 +1436,6 @@
   )
 )
 
-(define-method get_date_and_time
-  (of-object "EmpathyMessage")
-  (c-name "empathy_message_get_date_and_time")
-  (return-type "GDate*")
-  (parameters
-    '("time_t*" "timestamp")
-  )
-)
-
 (define-method should_highlight
   (of-object "EmpathyMessage")
   (c-name "empathy_message_should_highlight")
diff --git a/python/pyempathygtk/pyempathygtk.defs b/python/pyempathygtk/pyempathygtk.defs
index 2065e48..441a0f7 100644
--- a/python/pyempathygtk/pyempathygtk.defs
+++ b/python/pyempathygtk/pyempathygtk.defs
@@ -49,9 +49,15 @@
   (gtype-id "EMPATHY_TYPE_CHAT")
 )
 
-(define-object ChatView
+(define-object ChatTextView
   (in-module "Empathy")
   (parent "GtkTextView")
+  (c-name "EmpathyChatTextView")
+  (gtype-id "EMPATHY_TYPE_CHAT_TEXT_VIEW")
+)
+
+(define-object ChatView
+  (in-module "Empathy")
   (c-name "EmpathyChatView")
   (gtype-id "EMPATHY_TYPE_CHAT_VIEW")
 )
@@ -91,23 +97,16 @@
   (gtype-id "EMPATHY_TYPE_SMILEY_MANAGER")
 )
 
-(define-object Theme
-  (in-module "Empathy")
-  (parent "GObject")
-  (c-name "EmpathyTheme")
-  (gtype-id "EMPATHY_TYPE_THEME")
-)
-
 (define-object ThemeBoxes
   (in-module "Empathy")
-  (parent "EmpathyTheme")
+  (parent "EmpathyChatTextView")
   (c-name "EmpathyThemeBoxes")
   (gtype-id "EMPATHY_TYPE_THEME_BOXES")
 )
 
 (define-object ThemeIrc
   (in-module "Empathy")
-  (parent "EmpathyTheme")
+  (parent "EmpathyChatTextView")
   (c-name "EmpathyThemeIrc")
   (gtype-id "EMPATHY_TYPE_THEME_IRC")
 )
@@ -614,6 +613,57 @@
 
 
 
+;; From empathy-chat-text-view.h
+
+(define-function chat_text_view_get_type
+  (c-name "empathy_chat_text_view_get_type")
+  (return-type "GType")
+)
+
+(define-method get_last_contact
+  (of-object "EmpathyChatTextView")
+  (c-name "empathy_chat_text_view_get_last_contact")
+  (return-type "EmpathyContact*")
+)
+
+(define-method set_only_if_date
+  (of-object "EmpathyChatTextView")
+  (c-name "empathy_chat_text_view_set_only_if_date")
+  (return-type "none")
+  (parameters
+    '("gboolean" "only_if_date")
+  )
+)
+
+(define-method append_body
+  (of-object "EmpathyChatTextView")
+  (c-name "empathy_chat_text_view_append_body")
+  (return-type "none")
+  (parameters
+    '("const-gchar*" "body")
+    '("const-gchar*" "tag")
+  )
+)
+
+(define-method append_spacing
+  (of-object "EmpathyChatTextView")
+  (c-name "empathy_chat_text_view_append_spacing")
+  (return-type "none")
+)
+
+(define-method tag_set
+  (of-object "EmpathyChatTextView")
+  (c-name "empathy_chat_text_view_tag_set")
+  (return-type "GtkTextTag*")
+  (parameters
+    '("const-gchar*" "tag_name")
+    '("const-gchar*" "first_property_name")
+  )
+  (varargs #t)
+)
+
+
+
 ;; From empathy-chat-view.h
 
 (define-function chat_view_get_type
@@ -621,12 +671,6 @@
   (return-type "GType")
 )
 
-(define-function chat_view_new
-  (c-name "empathy_chat_view_new")
-  (is-constructor-of "EmpathyChatView")
-  (return-type "EmpathyChatView*")
-)
-
 (define-method append_message
   (of-object "EmpathyChatView")
   (c-name "empathy_chat_view_append_message")
@@ -645,17 +689,6 @@
   )
 )
 
-(define-method append_button
-  (of-object "EmpathyChatView")
-  (c-name "empathy_chat_view_append_button")
-  (return-type "none")
-  (parameters
-    '("const-gchar*" "message")
-    '("GtkWidget*" "button1")
-    '("GtkWidget*" "button2")
-  )
-)
-
 (define-method scroll
   (of-object "EmpathyChatView")
   (c-name "empathy_chat_view_scroll")
@@ -671,14 +704,10 @@
   (return-type "none")
 )
 
-(define-method get_selection_bounds
+(define-method get_has_selection
   (of-object "EmpathyChatView")
-  (c-name "empathy_chat_view_get_selection_bounds")
+  (c-name "empathy_chat_view_get_has_selection")
   (return-type "gboolean")
-  (parameters
-    '("GtkTextIter*" "start")
-    '("GtkTextIter*" "end")
-  )
 )
 
 (define-method clear
@@ -733,68 +762,6 @@
   (return-type "none")
 )
 
-(define-method get_theme
-  (of-object "EmpathyChatView")
-  (c-name "empathy_chat_view_get_theme")
-  (return-type "EmpathyTheme*")
-)
-
-(define-method set_theme
-  (of-object "EmpathyChatView")
-  (c-name "empathy_chat_view_set_theme")
-  (return-type "none")
-  (parameters
-    '("EmpathyTheme*" "theme")
-  )
-)
-
-(define-method set_margin
-  (of-object "EmpathyChatView")
-  (c-name "empathy_chat_view_set_margin")
-  (return-type "none")
-  (parameters
-    '("gint" "margin")
-  )
-)
-
-(define-function chat_view_get_smiley_menu
-  (c-name "empathy_chat_view_get_smiley_menu")
-  (return-type "GtkWidget*")
-  (parameters
-    '("GCallback" "callback")
-    '("gpointer" "user_data")
-  )
-)
-
-(define-method get_last_timestamp
-  (of-object "EmpathyChatView")
-  (c-name "empathy_chat_view_get_last_timestamp")
-  (return-type "time_t")
-)
-
-(define-method set_last_timestamp
-  (of-object "EmpathyChatView")
-  (c-name "empathy_chat_view_set_last_timestamp")
-  (return-type "none")
-  (parameters
-    '("time_t" "timestamp")
-  )
-)
-
-(define-method get_last_contact
-  (of-object "EmpathyChatView")
-  (c-name "empathy_chat_view_get_last_contact")
-  (return-type "EmpathyContact*")
-)
-
-(define-function chat_view_get_avatar_pixbuf_with_cache
-  (c-name "empathy_chat_view_get_avatar_pixbuf_with_cache")
-  (return-type "GdkPixbuf*")
-  (parameters
-    '("EmpathyContact*" "contact")
-  )
-)
-
 
 
 ;; From empathy-conf.h
@@ -1354,17 +1321,6 @@
   (varargs #t)
 )
 
-(define-method add_from_pixbuf
-  (of-object "EmpathySmileyManager")
-  (c-name "empathy_smiley_manager_add_from_pixbuf")
-  (return-type "none")
-  (parameters
-    '("GdkPixbuf*" "smiley")
-    '("const-gchar*" "first_str")
-  )
-  (varargs #t)
-)
-
 (define-method get_all
   (of-object "EmpathySmileyManager")
   (c-name "empathy_smiley_manager_get_all")
@@ -1380,6 +1336,17 @@
   )
 )
 
+(define-function smiley_menu_new
+  (c-name "empathy_smiley_menu_new")
+  (is-constructor-of "EmpathySmileyMenu")
+  (return-type "GtkWidget*")
+  (parameters
+    '("EmpathySmileyManager*" "manager")
+    '("EmpathySmileyMenuFunc" "func")
+    '("gpointer" "user_data")
+  )
+)
+
 (define-method free
   (of-object "EmpathySmiley")
   (c-name "empathy_smiley_free")
@@ -1464,100 +1431,10 @@
   (return-type "GType")
 )
 
-
-
-;; From empathy-theme.h
-
-(define-function theme_get_type
-  (c-name "empathy_theme_get_type")
-  (return-type "GType")
-)
-
-(define-method update_view
-  (of-object "EmpathyTheme")
-  (c-name "empathy_theme_update_view")
-  (return-type "none")
-  (parameters
-    '("EmpathyChatView*" "view")
-  )
-)
-
-(define-method append_message
-  (of-object "EmpathyTheme")
-  (c-name "empathy_theme_append_message")
-  (return-type "none")
-  (parameters
-    '("EmpathyChatView*" "view")
-    '("EmpathyMessage*" "msg")
-  )
-)
-
-(define-method append_text
-  (of-object "EmpathyTheme")
-  (c-name "empathy_theme_append_text")
-  (return-type "none")
-  (parameters
-    '("EmpathyChatView*" "view")
-    '("const-gchar*" "body")
-    '("const-gchar*" "tag")
-    '("const-gchar*" "link_tag")
-  )
-)
-
-(define-method append_spacing
-  (of-object "EmpathyTheme")
-  (c-name "empathy_theme_append_spacing")
-  (return-type "none")
-  (parameters
-    '("EmpathyChatView*" "view")
-  )
-)
-
-(define-method append_event
-  (of-object "EmpathyTheme")
-  (c-name "empathy_theme_append_event")
-  (return-type "none")
-  (parameters
-    '("EmpathyChatView*" "view")
-    '("const-gchar*" "str")
-  )
-)
-
-(define-method append_timestamp
-  (of-object "EmpathyTheme")
-  (c-name "empathy_theme_append_timestamp")
-  (return-type "none")
-  (parameters
-    '("EmpathyChatView*" "view")
-    '("EmpathyMessage*" "message")
-    '("gboolean" "show_date")
-    '("gboolean" "show_time")
-  )
-)
-
-(define-method maybe_append_date_and_time
-  (of-object "EmpathyTheme")
-  (c-name "empathy_theme_maybe_append_date_and_time")
-  (return-type "none")
-  (parameters
-    '("EmpathyChatView*" "view")
-    '("EmpathyMessage*" "message")
-  )
-)
-
-(define-method get_show_avatars
-  (of-object "EmpathyTheme")
-  (c-name "empathy_theme_get_show_avatars")
-  (return-type "gboolean")
-)
-
-(define-method set_show_avatars
-  (of-object "EmpathyTheme")
-  (c-name "empathy_theme_set_show_avatars")
-  (return-type "none")
-  (parameters
-    '("gboolean" "show")
-  )
+(define-function theme_boxes_new
+  (c-name "empathy_theme_boxes_new")
+  (is-constructor-of "EmpathyThemeBoxes")
+  (return-type "EmpathyThemeBoxes*")
 )
 
 
@@ -1569,6 +1446,12 @@
   (return-type "GType")
 )
 
+(define-function theme_irc_new
+  (c-name "empathy_theme_irc_new")
+  (is-constructor-of "EmpathyThemeIrc")
+  (return-type "EmpathyThemeIrc*")
+)
+
 
 
 ;; From empathy-theme-manager.h
@@ -1588,23 +1471,10 @@
   (return-type "const-gchar**")
 )
 
-(define-method apply
-  (of-object "EmpathyThemeManager")
-  (c-name "empathy_theme_manager_apply")
-  (return-type "none")
-  (parameters
-    '("EmpathyChatView*" "view")
-    '("const-gchar*" "theme")
-  )
-)
-
-(define-method apply_saved
+(define-method create_view
   (of-object "EmpathyThemeManager")
-  (c-name "empathy_theme_manager_apply_saved")
-  (return-type "none")
-  (parameters
-    '("EmpathyChatView*" "view")
-  )
+  (c-name "empathy_theme_manager_create_view")
+  (return-type "EmpathyChatView*")
 )
 
 
@@ -1728,6 +1598,15 @@
   )
 )
 
+(define-function filename_from_icon_name
+  (c-name "empathy_filename_from_icon_name")
+  (return-type "gchar*")
+  (parameters
+    '("const-gchar*" "icon_name")
+    '("GtkIconSize" "icon_size")
+  )
+)
+
 (define-function text_iter_forward_search
   (c-name "empathy_text_iter_forward_search")
   (return-type "gboolean")
@@ -1815,15 +1694,4 @@
   )
 )
 
-(define-function text_buffer_tag_set
-  (c-name "empathy_text_buffer_tag_set")
-  (return-type "GtkTextTag*")
-  (parameters
-    '("GtkTextBuffer*" "buffer")
-    '("const-gchar*" "tag_name")
-    '("const-gchar*" "first_property_name")
-  )
-  (varargs #t)
-)
-
 
diff --git a/python/pyempathygtk/pyempathygtk.override b/python/pyempathygtk/pyempathygtk.override
index 782741d..60edf72 100644
--- a/python/pyempathygtk/pyempathygtk.override
+++ b/python/pyempathygtk/pyempathygtk.override
@@ -29,7 +29,6 @@ headers
 #include "empathy-spell-dialog.h"
 #include "empathy-spell.h"
 #include "empathy-theme-boxes.h"
-#include "empathy-theme.h"
 #include "empathy-theme-irc.h"
 #include "empathy-theme-manager.h"
 #include "empathy-ui-utils.h"
diff --git a/python/update-binding.sh b/python/update-binding.sh
index e00c7ce..848422c 100755
--- a/python/update-binding.sh
+++ b/python/update-binding.sh
@@ -47,6 +47,7 @@ python /usr/share/pygobject/2.0/codegen/h2def.py	\
 	empathy-cell-renderer-expander.h	\
 	empathy-cell-renderer-text.h		\
 	empathy-chat.h				\
+	empathy-chat-text-view.h		\
 	empathy-chat-view.h			\
 	empathy-conf.h				\
 	empathy-contact-dialogs.h		\
@@ -65,7 +66,6 @@ python /usr/share/pygobject/2.0/codegen/h2def.py	\
 	empathy-spell.h				\
 	empathy-spell-dialog.h			\
 	empathy-theme-boxes.h			\
-	empathy-theme.h				\
 	empathy-theme-irc.h			\
 	empathy-theme-manager.h			\
 	empathy-ui-utils.h			\
diff --git a/src/Makefile.am b/src/Makefile.am
index c5944aa..b060ceb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,12 +7,14 @@ AM_CPPFLAGS =						\
 	-DLIBDIR=\""$(libdir)"\"			\
 	-DLOCALEDIR=\""$(datadir)/locale"\"		\
 	$(EMPATHY_CFLAGS)				\
+	$(WEBKIT_CFLAGS)				\
 	$(WARN_CFLAGS)
 
 LDADD =								\
 	$(top_builddir)/libempathy-gtk/libempathy-gtk.la	\
 	$(top_builddir)/libempathy/libempathy.la		\
 	$(top_builddir)/extensions/libemp-extensions.la		\
+	$(WEBKIT_LIBS)						\
 	$(EMPATHY_LIBS)
 
 bin_PROGRAMS =			\
diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c
index 7ae404e..c8867dc 100644
--- a/src/empathy-chat-window.c
+++ b/src/empathy-chat-window.c
@@ -49,6 +49,7 @@
 #include <libempathy-gtk/empathy-contact-dialogs.h>
 #include <libempathy-gtk/empathy-log-window.h>
 #include <libempathy-gtk/empathy-geometry.h>
+#include <libempathy-gtk/empathy-smiley-manager.h>
 #include <libempathy-gtk/empathy-ui-utils.h>
 
 #include "empathy-chat-window.h"
@@ -467,25 +468,20 @@ chat_window_chat_notify_cb (EmpathyChat *chat)
 }
 
 static void
-chat_window_insert_smiley_activate_cb (GtkWidget         *menuitem,
-				       EmpathyChatWindow *window)
+chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
+				       EmpathySmiley        *smiley,
+				       gpointer              window)
 {
-	EmpathyChatWindowPriv *priv;
+	EmpathyChatWindowPriv *priv = GET_PRIV (window);
 	EmpathyChat           *chat;
-	GtkTextBuffer        *buffer;
-	GtkTextIter           iter;
-	const gchar          *smiley;
-
-	priv = GET_PRIV (window);
+	GtkTextBuffer         *buffer;
+	GtkTextIter            iter;
 
 	chat = priv->current_chat;
 
-	smiley = g_object_get_data (G_OBJECT (menuitem), "smiley_text");
-
 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
 	gtk_text_buffer_get_end_iter (buffer, &iter);
-	gtk_text_buffer_insert (buffer, &iter,
-				smiley, -1);
+	gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
 }
 
 static void
@@ -658,14 +654,13 @@ chat_window_edit_activate_cb (GtkWidget        *menuitem,
 	}
 
 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
-	if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL)) {
+	if (gtk_text_buffer_get_has_selection (buffer)) {
 		gtk_widget_set_sensitive (priv->menu_edit_copy, TRUE);
 		gtk_widget_set_sensitive (priv->menu_edit_cut, TRUE);
 	} else {
 		gboolean selection;
 
-		selection = empathy_chat_view_get_selection_bounds (priv->current_chat->view, 
-								   NULL, NULL);
+		selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
 
 		gtk_widget_set_sensitive (priv->menu_edit_cut, FALSE);
 		gtk_widget_set_sensitive (priv->menu_edit_copy, selection);
@@ -1186,6 +1181,7 @@ empathy_chat_window_init (EmpathyChatWindow *window)
 	gint                   i;
 	GtkWidget             *chat_vbox;
 	gchar                 *filename;
+	EmpathySmileyManager  *smiley_manager;
 	EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
 		EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
 
@@ -1260,10 +1256,12 @@ empathy_chat_window_init (EmpathyChatWindow *window)
 	g_object_unref (accel_group);
 
 	/* Set up smiley menu */
-	menu = empathy_chat_view_get_smiley_menu (
-		G_CALLBACK (chat_window_insert_smiley_activate_cb),
-		window);
-	gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->menu_conv_insert_smiley), menu);
+	smiley_manager = empathy_smiley_manager_new ();
+	menu = empathy_smiley_menu_new (smiley_manager,
+					chat_window_insert_smiley_activate_cb,
+					window);
+	gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->menu_conv_insert_smiley),
+				   menu);
 
 	/* Set up signals we can't do with glade since we may need to
 	 * block/unblock them at some later stage.
diff --git a/src/empathy-preferences.c b/src/empathy-preferences.c
index 865fdc8..6242f41 100644
--- a/src/empathy-preferences.c
+++ b/src/empathy-preferences.c
@@ -39,6 +39,11 @@
 #include <libempathy-gtk/empathy-contact-list-store.h>
 #include <libempathy-gtk/empathy-gtk-enum-types.h>
 
+#ifdef HAVE_WEBKIT
+#include <libempathy-gtk/empathy-theme-adium.h>
+#include <libempathy-gtk/empathy-theme-adium-manager.h>
+#endif
+
 #include "empathy-preferences.h"
 
 typedef struct {
@@ -81,19 +86,13 @@ static gboolean preferences_languages_load_foreach       (GtkTreeModel
 static void     preferences_languages_cell_toggled_cb    (GtkCellRendererToggle  *cell,
 							  gchar                  *path_string,
 							  EmpathyPreferences      *preferences);
-static void     preferences_themes_setup                 (EmpathyPreferences      *preferences);
 static void     preferences_widget_sync_bool             (const gchar            *key,
 							  GtkWidget              *widget);
 static void     preferences_widget_sync_string           (const gchar            *key,
 							  GtkWidget              *widget);
-static void     preferences_widget_sync_string_combo     (const gchar            *key,
-							  GtkWidget              *widget);
 static void     preferences_notify_string_cb             (EmpathyConf             *conf,
 							  const gchar            *key,
 							  gpointer                user_data);
-static void     preferences_notify_string_combo_cb       (EmpathyConf             *conf,
-							  const gchar            *key,
-							  gpointer                user_data);
 static void     preferences_notify_bool_cb               (EmpathyConf             *conf,
 							  const gchar            *key,
 							  gpointer                user_data);
@@ -106,9 +105,6 @@ static void     preferences_hookup_toggle_button         (EmpathyPreferences
 static void     preferences_hookup_radio_button          (EmpathyPreferences      *preferences,
 							  const gchar            *key,
 							  GtkWidget              *widget);
-static void     preferences_hookup_string_combo          (EmpathyPreferences      *preferences,
-							  const gchar            *key,
-							  GtkWidget              *widget);
 static void     preferences_hookup_sensitivity           (EmpathyPreferences      *preferences,
 							  const gchar            *key,
 							  GtkWidget              *widget);
@@ -116,8 +112,6 @@ static void     preferences_toggle_button_toggled_cb     (GtkWidget
 							  gpointer                user_data);
 static void     preferences_radio_button_toggled_cb      (GtkWidget              *button,
 							  gpointer                user_data);
-static void     preferences_string_combo_changed_cb      (GtkWidget *button,
-							  gpointer                user_data);
 static void     preferences_destroy_cb                   (GtkWidget              *widget,
 							  EmpathyPreferences      *preferences);
 static void     preferences_response_cb                  (GtkWidget              *widget,
@@ -134,6 +128,7 @@ enum {
 enum {
 	COL_COMBO_VISIBLE_NAME,
 	COL_COMBO_NAME,
+	COL_COMBO_PATH, // Only used for Adium themes
 	COL_COMBO_COUNT
 };
 
@@ -199,10 +194,6 @@ preferences_setup_widgets (EmpathyPreferences *preferences)
 					  EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
 					  preferences->checkbutton_show_smileys);
 
-	preferences_hookup_string_combo (preferences,
-					 EMPATHY_PREFS_CHAT_THEME,
-					 preferences->combobox_chat_theme);
-
 	preferences_hookup_radio_button (preferences,
 					 EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM,
 					 preferences->radiobutton_contact_list_sort_by_name);
@@ -466,33 +457,6 @@ preferences_languages_cell_toggled_cb (GtkCellRendererToggle *cell,
 	preferences_languages_save (preferences);
 }
 
-static void
-preferences_themes_setup (EmpathyPreferences *preferences)
-{
-	GtkComboBox   *combo;
-	GtkListStore  *model;
-	GtkTreeIter    iter;
-	const gchar  **themes;
-	gint           i;
-
-	combo = GTK_COMBO_BOX (preferences->combobox_chat_theme);
-
-	model = gtk_list_store_new (COL_COMBO_COUNT,
-				    G_TYPE_STRING,
-				    G_TYPE_STRING);
-
-	themes = empathy_theme_manager_get_themes ();
-	for (i = 0; themes[i]; i += 2) {
-		gtk_list_store_append (model, &iter);
-		gtk_list_store_set (model, &iter,
-				    COL_COMBO_VISIBLE_NAME, _(themes[i + 1]),
-				    COL_COMBO_NAME, themes[i],
-				    -1);
-	}
-
-	gtk_combo_box_set_model (combo, GTK_TREE_MODEL (model));
-	g_object_unref (model);
-}
 
 static void
 preferences_widget_sync_bool (const gchar *key, GtkWidget *widget)
@@ -540,51 +504,6 @@ preferences_widget_sync_string (const gchar *key, GtkWidget *widget)
 }
 
 static void
-preferences_widget_sync_string_combo (const gchar *key, GtkWidget *widget)
-{
-	gchar        *value;
-	GtkTreeModel *model;
-	GtkTreeIter   iter;
-	gboolean      found;
-
-	if (!empathy_conf_get_string (empathy_conf_get (), key, &value)) {
-		return;
-	}
-
-	model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
-
-	found = FALSE;
-	if (value && gtk_tree_model_get_iter_first (model, &iter)) {
-		gchar *name;
-
-		do {
-			gtk_tree_model_get (model, &iter,
-					    COL_COMBO_NAME, &name,
-					    -1);
-
-			if (strcmp (name, value) == 0) {
-				found = TRUE;
-				gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter);
-				break;
-			} else {
-				found = FALSE;
-			}
-
-			g_free (name);
-		} while (gtk_tree_model_iter_next (model, &iter));
-	}
-
-	/* Fallback to the first one. */
-	if (!found) {
-		if (gtk_tree_model_get_iter_first (model, &iter)) {
-			gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter);
-		}
-	}
-
-	g_free (value);
-}
-
-static void
 preferences_notify_string_cb (EmpathyConf  *conf,
 			      const gchar *key,
 			      gpointer     user_data)
@@ -592,13 +511,6 @@ preferences_notify_string_cb (EmpathyConf  *conf,
 	preferences_widget_sync_string (key, user_data);
 }
 
-static void
-preferences_notify_string_combo_cb (EmpathyConf  *conf,
-				    const gchar *key,
-				    gpointer     user_data)
-{
-	preferences_widget_sync_string_combo (key, user_data);
-}
 
 static void
 preferences_notify_bool_cb (EmpathyConf  *conf,
@@ -777,32 +689,6 @@ preferences_hookup_radio_button (EmpathyPreferences *preferences,
 }
 
 static void
-preferences_hookup_string_combo (EmpathyPreferences *preferences,
-				 const gchar       *key,
-				 GtkWidget         *widget)
-{
-	guint id;
-
-	preferences_widget_sync_string_combo (key, widget);
-
-	g_object_set_data_full (G_OBJECT (widget), "key",
-				g_strdup (key), g_free);
-
-	g_signal_connect (widget,
-			  "changed",
-			  G_CALLBACK (preferences_string_combo_changed_cb),
-			  NULL);
-
-	id = empathy_conf_notify_add (empathy_conf_get (),
-				      key,
-				      preferences_notify_string_combo_cb,
-				      widget);
-	if (id) {
-		preferences_add_id (preferences, id);
-	}
-}
-
-static void
 preferences_hookup_sensitivity (EmpathyPreferences *preferences,
 				const gchar       *key,
 				GtkWidget         *widget)
@@ -875,24 +761,152 @@ preferences_radio_button_toggled_cb (GtkWidget *button,
 }
 
 static void
-preferences_string_combo_changed_cb (GtkWidget *combo,
-				     gpointer   user_data)
+preferences_theme_notify_cb (EmpathyConf *conf,
+			     const gchar *key,
+			     gpointer     user_data)
+{
+	EmpathyPreferences *preferences = user_data;
+	GtkComboBox        *combo;
+	gchar              *value;
+	GtkTreeModel       *model;
+	GtkTreeIter         iter;
+	gboolean            found = FALSE;
+	gchar		   *path;
+	gchar		   *conf_path;
+
+	if (!empathy_conf_get_string (conf, key, &value)) {
+		return;
+	}
+	if (!empathy_conf_get_string (conf, EMPATHY_PREFS_CHAT_ADIUM_PATH, &conf_path)) {
+		return;
+	}
+
+	combo = GTK_COMBO_BOX (preferences->combobox_chat_theme);
+	model = gtk_combo_box_get_model (combo);
+	if (value && conf_path && gtk_tree_model_get_iter_first (model, &iter)) {
+		gchar *name;
+
+		do {
+			gtk_tree_model_get (model, &iter,
+					    COL_COMBO_NAME, &name,
+					    COL_COMBO_PATH, &path,
+					    -1);
+			if (strcmp (name, value) == 0 && strcmp (path, conf_path) == 0) {
+				found = TRUE;
+				gtk_combo_box_set_active_iter (combo, &iter);
+				break;
+			}
+
+			g_free (name);
+			g_free (path);
+		} while (gtk_tree_model_iter_next (model, &iter));
+	}
+
+	/* Fallback to the first one. */
+	if (!found) {
+		if (gtk_tree_model_get_iter_first (model, &iter)) {
+			gtk_combo_box_set_active_iter (combo, &iter);
+		}
+	}
+
+	g_free (value);
+	g_free (conf_path);
+}
+
+static void
+preferences_theme_changed_cb (GtkComboBox        *combo,
+			      EmpathyPreferences *preferences)
 {
-	const gchar  *key;
 	GtkTreeModel *model;
 	GtkTreeIter   iter;
 	gchar        *name;
+	gchar        *path;
 
-	key = g_object_get_data (G_OBJECT (combo), "key");
-
-	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) {
-		model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+	if (gtk_combo_box_get_active_iter (combo, &iter)) {
+		model = gtk_combo_box_get_model (combo);
 
 		gtk_tree_model_get (model, &iter,
 				    COL_COMBO_NAME, &name,
+				    COL_COMBO_PATH, &path,
 				    -1);
-		empathy_conf_set_string (empathy_conf_get (), key, name);
+		empathy_conf_set_string (empathy_conf_get (),
+					 EMPATHY_PREFS_CHAT_THEME,
+					 name);
+		empathy_conf_set_string (empathy_conf_get (),
+					 EMPATHY_PREFS_CHAT_ADIUM_PATH,
+					 path);
 		g_free (name);
+		g_free (path);
+	}
+}
+
+static void
+preferences_themes_setup (EmpathyPreferences *preferences)
+{
+	GtkComboBox   *combo;
+	GtkListStore  *model;
+	GtkTreeIter    iter;
+	const gchar  **themes;
+	gint           i;
+	guint          id;
+
+	/* Setup the combobox */
+	combo = GTK_COMBO_BOX (preferences->combobox_chat_theme);
+	model = gtk_list_store_new (COL_COMBO_COUNT,
+				    G_TYPE_STRING,
+				    G_TYPE_STRING,
+				    G_TYPE_STRING);
+
+	themes = empathy_theme_manager_get_themes ();
+	for (i = 0; themes[i]; i += 2) {
+		gtk_list_store_append (model, &iter);
+		gtk_list_store_set (model, &iter,
+				    COL_COMBO_VISIBLE_NAME, _(themes[i + 1]),
+				    COL_COMBO_NAME, themes[i],
+				    COL_COMBO_PATH, g_strdup(""),
+				    -1);
+	}
+
+#ifdef HAVE_WEBKIT
+        EmpathyThemeAdiumManager *manager;
+        manager = g_object_new (EMPATHY_TYPE_THEME_ADIUM_MANAGER, NULL);
+	GList *adium_themes = empathy_theme_adium_manager_get_themes (manager);	
+	GList *adium_iter = NULL;
+	EmpathyThemeAdiumDetails *details = NULL;
+	
+	adium_iter = g_list_first (adium_themes);
+	while (adium_iter) {
+		details = (EmpathyThemeAdiumDetails*) adium_iter->data;
+		gtk_list_store_append (model, &iter);
+		gtk_list_store_set (model, &iter,
+				    COL_COMBO_VISIBLE_NAME, details->name,
+				    COL_COMBO_NAME, g_strdup(EMPATHY_THEME_ADIUM_NAME),
+				    COL_COMBO_PATH, details->path,
+				    -1);
+		adium_iter = g_list_next (adium_iter);
+	}
+	g_object_unref (manager);
+#endif 
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
+					      COL_COMBO_VISIBLE_NAME, 
+					      GTK_SORT_ASCENDING);
+	gtk_combo_box_set_model (combo, GTK_TREE_MODEL (model));
+	g_object_unref (model);
+
+	g_signal_connect (combo, "changed",
+			  G_CALLBACK (preferences_theme_changed_cb),
+			  preferences);
+
+	/* Select the theme from the gconf key and track changes */
+	preferences_theme_notify_cb (empathy_conf_get (),
+				     EMPATHY_PREFS_CHAT_THEME,
+				     preferences);
+	id = empathy_conf_notify_add (empathy_conf_get (),
+				      EMPATHY_PREFS_CHAT_THEME,
+				      preferences_theme_notify_cb,
+				      preferences);
+	if (id) {
+		preferences_add_id (preferences, id);
 	}
 }
 
diff --git a/src/empathy-preferences.glade b/src/empathy-preferences.glade
index fc4e13c..cd2270a 100644
--- a/src/empathy-preferences.glade
+++ b/src/empathy-preferences.glade
@@ -535,6 +535,13 @@
                                   </packing>
                                 </child>
                               </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <placeholder/>
                             </child>
                           </widget>
                         </child>
@@ -551,10 +558,6 @@
                       </packing>
                     </child>
                   </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                  </packing>
                 </child>
               </widget>
               <packing>
