changeset 8061:ea073d234191

[gaim-migrate @ 8749] <b><i><u>what you see is what you get</u></i></b> committer: Tailor Script <tailor@pidgin.im>
author Nathan Walp <nwalp@pidgin.im>
date Sat, 10 Jan 2004 06:06:02 +0000
parents b66733e6e6f2
children 3c4313120c63
files ChangeLog VERSION configure.ac src/dialogs.c src/gtkconv.c src/gtkimhtml.c src/gtkimhtml.h src/gtkutils.c
diffstat 8 files changed, 1073 insertions(+), 451 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Jan 10 05:20:50 2004 +0000
+++ b/ChangeLog	Sat Jan 10 06:06:02 2004 +0000
@@ -1,5 +1,8 @@
 Gaim: The Pimpin' Penguin IM Clone that's good for the soul!
 
+version 0.76cvs:
+	* WYSIWYG text input (with scrollbars, too!)
+
 version 0.75 (01/09/2004):
 	* New Yahoo! auth method
 	* Yahoo! file transfer (Tim Ringenbach)
--- a/VERSION	Sat Jan 10 05:20:50 2004 +0000
+++ b/VERSION	Sat Jan 10 06:06:02 2004 +0000
@@ -1,1 +1,1 @@
-0.75
+0.76cvs
--- a/configure.ac	Sat Jan 10 05:20:50 2004 +0000
+++ b/configure.ac	Sat Jan 10 06:06:02 2004 +0000
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 AC_INIT(src/main.c)
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE([gaim], [0.75])
+AM_INIT_AUTOMAKE([gaim], [0.76cvs])
 
 AC_PREREQ([2.50])
 
--- a/src/dialogs.c	Sat Jan 10 05:20:50 2004 +0000
+++ b/src/dialogs.c	Sat Jan 10 06:06:02 2004 +0000
@@ -695,7 +695,6 @@
 static void do_insert_link(GtkWidget *w, int resp, struct linkdlg *a)
 {
 	GaimGtkConversation *gtkconv;
-	char *open_tag;
 	const char *urltext, *showtext;
 
 	gtkconv = GAIM_GTK_CONVERSATION(a->c);
@@ -707,9 +706,8 @@
 		if (!strlen(showtext))
 			showtext = urltext;
 
-		open_tag = g_strdup_printf("<A HREF=\"%s\">%s", urltext, showtext);
-		gaim_gtk_surround(gtkconv, open_tag, "</A>");
-		g_free(open_tag);
+		gtk_imhtml_insert_link(GTK_IMHTML(gtkconv->entry), urltext, showtext);
+		gaim_gtk_advance_past(gtkconv, "<A HREF>", "</A>");
 	}
 
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkconv->toolbar.link), FALSE);
@@ -863,16 +861,12 @@
 	gtkconv = GAIM_GTK_CONVERSATION(c);
 
 	gtkconv->fg_color = text_color;
-	g_snprintf(open_tag, 23, "<FONT COLOR=\"#%02X%02X%02X\">",
+	g_snprintf(open_tag, 23, "#%02X%02X%02X",
 			   text_color.red / 256,
 			   text_color.green / 256,
 			   text_color.blue / 256);
-	gaim_gtk_surround(gtkconv, open_tag, "</FONT>");
+	gtk_imhtml_toggle_forecolor(GTK_IMHTML(gtkconv->entry), open_tag);
 
-	gaim_debug(GAIM_DEBUG_MISC, "fgcolor dialog", "#%02X%02X%02X\n",
-			   text_color.red / 256,
-			   text_color.green / 256,
-			   text_color.blue / 256);
 	g_free(open_tag);
 	cancel_fgcolor(NULL, c);
 }
@@ -894,16 +888,12 @@
 	gtkconv = GAIM_GTK_CONVERSATION(c);
 
 	gtkconv->bg_color = text_color;
-	g_snprintf(open_tag, 25, "<BODY BGCOLOR=\"#%02X%02X%02X\">",
+	g_snprintf(open_tag, 25, "#%02X%02X%02X",
 			   text_color.red / 256,
 			   text_color.green / 256,
 			   text_color.blue / 256);
-	gaim_gtk_surround(gtkconv, open_tag, "</BODY>");
-	gaim_debug(GAIM_DEBUG_MISC, "bgcolor dialog", "#%02X%02X%02X\n",
-			   text_color.red / 256,
-			   text_color.green / 256,
-			   text_color.blue / 256);
-
+	gtk_imhtml_toggle_backcolor(GTK_IMHTML(gtkconv->entry), open_tag);
+	
 	g_free(open_tag);
 	cancel_bgcolor(NULL, c);
 }
@@ -1351,21 +1341,12 @@
 {
 	GaimGtkConversation *gtkconv;
 	char *smiley_text = g_object_get_data(G_OBJECT(widget), "smiley_text");
-	GtkTextMark *select_mark, *insert_mark;
-	GtkTextIter select_iter, insert_iter;
+	GaimPlugin *proto = gaim_find_prpl(gaim_account_get_protocol_id(gaim_conversation_get_account(c)));
 
 	gtkconv = GAIM_GTK_CONVERSATION(c);
 
-	select_mark = gtk_text_buffer_get_selection_bound(gtkconv->entry_buffer);
-	insert_mark = gtk_text_buffer_get_insert(gtkconv->entry_buffer);
+	gtk_imhtml_insert_smiley(GTK_IMHTML(gtkconv->entry), proto->info->name, smiley_text);
 
-	if(insert_mark != select_mark) { /* there is text selected */
-		gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &select_iter, select_mark);
-		gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &insert_iter, insert_mark);
-		gtk_text_buffer_delete(gtkconv->entry_buffer, &select_iter, &insert_iter);
-	}
-
-	gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, smiley_text, -1);
 	close_smiley_dialog(NULL, c);
 }
 
--- a/src/gtkconv.c	Sat Jan 10 05:20:50 2004 +0000
+++ b/src/gtkconv.c	Sat Jan 10 06:06:02 2004 +0000
@@ -353,23 +353,73 @@
 	return FALSE;
 }
 
+static void default_formatize(GaimConversation *conv) {
+	GaimGtkConversation *c = GAIM_GTK_CONVERSATION(conv);
+	GaimConnection *gc = gaim_conversation_get_gc(conv);
+		
+	if (gc && gc->flags & GAIM_CONNECTION_HTML) {
+		if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_bold")) {
+			gtk_imhtml_toggle_bold(GTK_IMHTML(c->entry));
+		}
+		
+		if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_italic")) {
+			gtk_imhtml_toggle_italic(GTK_IMHTML(c->entry));
+		}
+
+		if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_underline")) {
+			gtk_imhtml_toggle_underline(GTK_IMHTML(c->entry));
+		}
+
+		if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_strikethrough")) {
+			/* Tell me noone uses <s> by default ... maybe I won't do
+			 _toggle_strikethrough and not let them */
+			/*	g_snprintf(buf2, limit, "<STRIKE>%s</STRIKE>", buf);
+			  strcpy(buf, buf2); */
+		}
+
+		if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_font") || c->has_font) {
+			gtk_imhtml_toggle_fontface(GTK_IMHTML(c->entry), c->fontface);
+		}
+
+		if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_size")) {
+		        gtk_imhtml_font_set_size(GTK_IMHTML(c->entry), gaim_prefs_get_int("/gaim/gtk/conversations/font_size"));
+		}
+
+		if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_fgcolor")) {
+			char *color = g_strdup_printf("#%02x%02x%02x", 
+						      c->fg_color.red   / 256,
+						      c->fg_color.green / 256,
+						      c->fg_color.blue  / 256);
+			gtk_imhtml_toggle_forecolor(GTK_IMHTML(c->entry), color);
+			g_free(color);
+		}
+
+		if (!(gc->flags & GAIM_CONNECTION_NO_BGCOLOR) && gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_bgcolor")) {
+			char *color = g_strdup_printf("#%02x%02x%02x", 
+						      c->bg_color.red   / 256,
+						      c->bg_color.green / 256,
+						      c->bg_color.blue  / 256);
+			gtk_imhtml_toggle_backcolor(GTK_IMHTML(c->entry), color);
+			g_free(color);	
+		}
+	}
+}
+
 static void
 send_cb(GtkWidget *widget, GaimConversation *conv)
 {
 	GaimGtkConversation *gtkconv;
-	char *buf, *buf2;
-	GtkTextIter start_iter, end_iter;
-	int limit;
+	char *buf;
 	GaimConnection *gc = gaim_conversation_get_gc(conv);
 
 	gtkconv = GAIM_GTK_CONVERSATION(conv);
-
-	gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &start_iter);
-	gtk_text_buffer_get_end_iter(gtkconv->entry_buffer, &end_iter);
-	buf2 = gtk_text_buffer_get_text(gtkconv->entry_buffer,
-									&start_iter, &end_iter, FALSE);
-
-	set_toggle(gtkconv->toolbar.bold,        FALSE);
+	
+	if (gc && gc->flags & GAIM_CONNECTION_HTML)
+		buf = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry));
+	else
+		buf = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry));
+
+	/*	set_toggle(gtkconv->toolbar.bold,        FALSE);
 	set_toggle(gtkconv->toolbar.italic,      FALSE);
 	set_toggle(gtkconv->toolbar.underline,   FALSE);
 	set_toggle(gtkconv->toolbar.larger_size, FALSE);
@@ -379,85 +429,18 @@
 	set_toggle(gtkconv->toolbar.fgcolor,     FALSE);
 	set_toggle(gtkconv->toolbar.bgcolor,     FALSE);
 	set_toggle(gtkconv->toolbar.link,        FALSE);
-
+	*/
 	gtk_widget_grab_focus(gtkconv->entry);
 
-	limit = 32 * 1024; /* This will be done again in gaim_conv_im_send. *shrug* */
-
-	buf = g_malloc(limit);
-	strncpy(buf, buf2, limit);
-
-	g_free(buf2);
-
 	if (strlen(buf) == 0) {
 		g_free(buf);
 
 		return;
 	}
 
-	buf2 = g_malloc(limit);
-
-	if (gc && gc->flags & GAIM_CONNECTION_HTML) {
-		if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_bold")) {
-			g_snprintf(buf2, limit, "<B>%s</B>", buf);
-			strcpy(buf, buf2);
-		}
-
-		if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_italic")) {
-			g_snprintf(buf2, limit, "<I>%s</I>", buf);
-			strcpy(buf, buf2);
-		}
-
-		if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_underline")) {
-			g_snprintf(buf2, limit, "<U>%s</U>", buf);
-			strcpy(buf, buf2);
-		}
-
-		if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_strikethrough")) {
-			g_snprintf(buf2, limit, "<STRIKE>%s</STRIKE>", buf);
-			strcpy(buf, buf2);
-		}
-
-		if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_font") ||
-			gtkconv->has_font) {
-
-			g_snprintf(buf2, limit,
-					   "<FONT FACE=\"%s\">%s</FONT>", gtkconv->fontface, buf);
-			strcpy(buf, buf2);
-		}
-
-		if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_size")) {
-			g_snprintf(buf2, limit,
-					   "<FONT SIZE=\"%d\">%s</FONT>",
-					   gaim_prefs_get_int("/gaim/gtk/conversations/font_size"),
-					   buf);
-			strcpy(buf, buf2);
-		}
-
-		if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_fgcolor")) {
-			g_snprintf(buf2, limit,
-					   "<FONT COLOR=\"#%02X%02X%02X\">%s</FONT>",
-					   gtkconv->fg_color.red   / 256,
-					   gtkconv->fg_color.green / 256,
-					   gtkconv->fg_color.blue  / 256, buf);
-			strcpy(buf, buf2);
-		}
-
-		if (!(gc->flags & GAIM_CONNECTION_NO_BGCOLOR) && gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_bgcolor")) {
-			g_snprintf(buf2, limit,
-					   "<BODY BGCOLOR=\"#%02X%02X%02X\">%s</BODY>",
-					   gtkconv->bg_color.red   / 256,
-					   gtkconv->bg_color.green / 256,
-					   gtkconv->bg_color.blue  / 256, buf);
-			strcpy(buf, buf2);
-		}
-	}
-
-	g_free(buf2);
-
 	if (gaim_conversation_get_type(conv) == GAIM_CONV_IM)
 		gaim_conv_im_send(GAIM_CONV_IM(conv), buf);
-	else
+	else if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT)
 		gaim_conv_chat_send(GAIM_CONV_CHAT(conv), buf);
 
 	if (gaim_prefs_get_bool("/gaim/gtk/conversations/im/hide_on_send"))
@@ -465,7 +448,8 @@
 
 	g_free(buf);
 
-	gtk_text_buffer_set_text(gtkconv->entry_buffer, "", -1);
+	gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry));
+	default_formatize(conv);
 }
 
 static void
@@ -1353,8 +1337,8 @@
 					conv->send_history->next->data) {
 
 					conv->send_history = conv->send_history->next;
-					gtk_text_buffer_set_text(gtkconv->entry_buffer,
-											 conv->send_history->data, -1);
+					gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry));
+					gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->entry), conv->send_history->data, 0, NULL);
 				}
 
 				break;
@@ -1366,9 +1350,10 @@
 				if (conv->send_history->prev) {
 					conv->send_history = conv->send_history->prev;
 
-					if (conv->send_history->data)
-						gtk_text_buffer_set_text(gtkconv->entry_buffer,
-												 conv->send_history->data, -1);
+					if (conv->send_history->data) {
+						gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry));
+						gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->entry), conv->send_history->data, 0, NULL);
+					}
 				}
 
 				break;
@@ -2304,66 +2289,35 @@
 static void
 do_bold(GtkWidget *bold, GaimGtkConversation *gtkconv)
 {
-	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bold)))
-		gaim_gtk_surround(gtkconv, "<B>", "</B>");
-	else
-		gaim_gtk_advance_past(gtkconv, "<B>", "</B>");
-
+	gtk_imhtml_toggle_bold(GTK_IMHTML(gtkconv->entry));
 	gtk_widget_grab_focus(gtkconv->entry);
 }
 
 static void
 do_italic(GtkWidget *italic, GaimGtkConversation *gtkconv)
 {
-	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(italic)))
-		gaim_gtk_surround(gtkconv, "<I>", "</I>");
-	else
-		gaim_gtk_advance_past(gtkconv, "<I>", "</I>");
-
+	gtk_imhtml_toggle_italic(GTK_IMHTML(gtkconv->entry));
 	gtk_widget_grab_focus(gtkconv->entry);
 }
 
 static void
 do_underline(GtkWidget *underline, GaimGtkConversation *gtkconv)
 {
-	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(underline)))
-		gaim_gtk_surround(gtkconv, "<U>", "</U>");
-	else
-		gaim_gtk_advance_past(gtkconv, "<U>", "</U>");
-
+	gtk_imhtml_toggle_underline(GTK_IMHTML(gtkconv->entry));
 	gtk_widget_grab_focus(gtkconv->entry);
 }
 
 static void
 do_small(GtkWidget *smalltb, GaimGtkConversation *gtkconv)
 {
-	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(smalltb)))
-		gaim_gtk_surround(gtkconv, "<FONT SIZE=\"1\">", "</FONT>");
-	else
-		gaim_gtk_advance_past(gtkconv, "<FONT SIZE=\"1\">", "</FONT>");
-
-	gtk_widget_grab_focus(gtkconv->entry);
-}
-
-static void
-do_normal(GtkWidget *normal, GaimGtkConversation *gtkconv)
-{
-	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(normal)))
-		gaim_gtk_surround(gtkconv, "<FONT SIZE=\"3\">", "</FONT>");
-	else
-		gaim_gtk_advance_past(gtkconv, "<FONT SIZE=\"3\">", "</FONT>");
-
+	gtk_imhtml_font_shrink(GTK_IMHTML(gtkconv->entry));
 	gtk_widget_grab_focus(gtkconv->entry);
 }
 
 static void
 do_big(GtkWidget *large, GaimGtkConversation *gtkconv)
 {
-	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(large)))
-		gaim_gtk_surround(gtkconv, "<FONT SIZE=\"5\">", "</FONT>");
-	else
-		gaim_gtk_advance_past(gtkconv, "<FONT SIZE=\"5\">", "</FONT>");
-
+	gtk_imhtml_font_grow(GTK_IMHTML(gtkconv->entry));
 	gtk_widget_grab_focus(gtkconv->entry);
 }
 
@@ -2374,12 +2328,7 @@
 
 	gtkconv = GAIM_GTK_CONVERSATION(conv);
 
-	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(font)))
-		show_font_dialog(conv, font);
-	else if (gtkconv->dialogs.font != NULL)
-		cancel_font(font, conv);
-	else
-		gaim_gtk_advance_past(gtkconv, "<FONT FACE>", "</FONT>");
+	show_font_dialog(conv, font);
 }
 
 static void
@@ -3440,7 +3389,7 @@
 
 	gtkconv->toolbar.larger_size = button;
 
-	/* Normal font size */
+	/* Normal font size 
 	button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_TEXT_NORMAL);
 	gtk_size_group_add_widget(sg, button);
 	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
@@ -3451,6 +3400,7 @@
 					 G_CALLBACK(do_normal), gtkconv);
 
 	gtkconv->toolbar.normal_size = button;
+	*/
 
 	/* Decrease font size */
 	button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_TEXT_SMALLER);
@@ -3773,14 +3723,17 @@
 	/* Setup the entry widget. */
 	sw = gtk_scrolled_window_new(NULL, NULL);
 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
+								   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
+										GTK_SHADOW_IN);
 	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
 	gtk_widget_show(sw);
 
-	gtkconv->entry_buffer = gtk_text_buffer_new(NULL);
-	gtkconv->entry = gtk_text_view_new_with_buffer(gtkconv->entry_buffer);
-
+	gtkconv->entry = gtk_imhtml_new(NULL, NULL);
+	gtkconv->entry_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
+	gaim_setup_imhtml(gtkconv->entry);
+	gtk_imhtml_set_editable(GTK_IMHTML(gtkconv->entry), TRUE);
+	default_formatize(conv);
 	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(gtkconv->entry), GTK_WRAP_WORD_CHAR);
 	gtk_widget_set_size_request(gtkconv->entry, -1,
 			MAX(gaim_prefs_get_int("/gaim/gtk/conversations/chat/entry_height"),
@@ -3874,14 +3827,17 @@
 	/* Setup the entry widget. */
 	sw = gtk_scrolled_window_new(NULL, NULL);
 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
+								   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
+										GTK_SHADOW_IN);
 	gtk_box_pack_start(GTK_BOX(vbox2), sw, TRUE, TRUE, 0);
 	gtk_widget_show(sw);
 
-	gtkconv->entry_buffer = gtk_text_buffer_new(NULL);
-	gtkconv->entry = gtk_text_view_new_with_buffer(gtkconv->entry_buffer);
-
+	gtkconv->entry = gtk_imhtml_new(NULL, NULL);
+	gtkconv->entry_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
+	gaim_setup_imhtml(gtkconv->entry);
+	gtk_imhtml_set_editable(GTK_IMHTML(gtkconv->entry), TRUE);
+	default_formatize(conv);
 	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(gtkconv->entry), GTK_WRAP_WORD_CHAR);
 	gtk_widget_set_size_request(gtkconv->entry, -1,
 			MAX(gaim_prefs_get_int("/gaim/gtk/conversations/im/entry_height"),
--- a/src/gtkimhtml.c	Sat Jan 10 05:20:50 2004 +0000
+++ b/src/gtkimhtml.c	Sat Jan 10 06:06:02 2004 +0000
@@ -61,12 +61,30 @@
 
 #define TOOLTIP_TIMEOUT 500
 
+static void insert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml);
+void gtk_imhtml_close_tags(GtkIMHtml *imhtml);
+
 /* POINT_SIZE converts from AIM font sizes to point sizes.  It probably should be redone in such a
  * way that it base the sizes off the default font size rather than using arbitrary font sizes. */
 #define MAX_FONT_SIZE 7
 #define POINT_SIZE(x) (options & GTK_IMHTML_USE_POINTSIZE ? x : _point_sizes [MIN ((x), MAX_FONT_SIZE) - 1])
 static gint _point_sizes [] = { 8, 10, 12, 14, 20, 30, 40 };
 
+enum {
+	TARGET_HTML,
+	TARGET_UTF8_STRING,
+	TARGET_COMPOUND_TEXT,
+	TARGET_STRING,
+	TARGET_TEXT
+};
+
+GtkTargetEntry selection_targets[] = {
+	{ "text/html", 0, TARGET_HTML },
+	{ "UTF8_STRING", 0, TARGET_UTF8_STRING },
+	{ "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
+	{ "STRING", 0, TARGET_STRING },
+	{ "TEXT", 0, TARGET_TEXT}};
+
 static GtkSmileyTree*
 gtk_smiley_tree_new ()
 {
@@ -98,12 +116,12 @@
 			t->children [index] = g_new0 (GtkSmileyTree, 1);
 		} else
 			index = GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str);
-		
+
 		t = t->children [index];
-		
+
 		x++;
 	}
-	
+
 	t->image = smiley;
 }
 
@@ -155,7 +173,7 @@
 
 	layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip);
 
-	gtk_paint_flat_box (imhtml->tip_window->style, imhtml->tip_window->window, 
+	gtk_paint_flat_box (imhtml->tip_window->style, imhtml->tip_window->window,
 						GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, imhtml->tip_window,
 						"tooltip", 0, 0, -1, -1);
 
@@ -181,7 +199,7 @@
 		imhtml->tip_timer = 0;
 		return FALSE;
 	}
-	
+
 	if (imhtml->tip_window){
 		gtk_widget_destroy (imhtml->tip_window);
 		imhtml->tip_window = NULL;
@@ -203,12 +221,12 @@
 
 
 	pango_layout_get_pixel_size(layout, &scr_w, NULL);
-	gap = PANGO_PIXELS((pango_font_metrics_get_ascent(font) + 
+	gap = PANGO_PIXELS((pango_font_metrics_get_ascent(font) +
 					   pango_font_metrics_get_descent(font))/ 4);
 
 	if (gap < 2)
 		gap = 2;
-	baseline_skip = PANGO_PIXELS(pango_font_metrics_get_ascent(font) + 
+	baseline_skip = PANGO_PIXELS(pango_font_metrics_get_ascent(font) +
 								pango_font_metrics_get_descent(font));
 	w = 8 + scr_w;
 	h = 8 + baseline_skip;
@@ -226,7 +244,7 @@
 	else if (x < 0)
 		x = 0;
 
-	y = y + PANGO_PIXELS(pango_font_metrics_get_ascent(font) + 
+	y = y + PANGO_PIXELS(pango_font_metrics_get_ascent(font) +
 						pango_font_metrics_get_descent(font));
 
 	gtk_widget_set_size_request (imhtml->tip_window, w, h);
@@ -240,7 +258,7 @@
 }
 
 gboolean gtk_motion_event_notify(GtkWidget *imhtml, GdkEventMotion *event, gpointer data)
-{	
+{
 	GtkTextIter iter;
 	GdkWindow *win = event->window;
 	int x, y;
@@ -260,7 +278,7 @@
 			break;
 		templist = templist->next;
 	}
-		
+
 	if (GTK_IMHTML(imhtml)->tip) {
 		if ((tip == GTK_IMHTML(imhtml)->tip)) {
 			return FALSE;
@@ -270,18 +288,22 @@
 			gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window);
 			GTK_IMHTML(imhtml)->tip_window = NULL;
 		}
-		gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->arrow_cursor);
+		if (GTK_IMHTML(imhtml)->editable)
+			gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->text_cursor);
+		else
+			gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->arrow_cursor);
 		if (GTK_IMHTML(imhtml)->tip_timer)
 			g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
 		GTK_IMHTML(imhtml)->tip_timer = 0;
 	}
-	
+
 	if(tip){
-		gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->hand_cursor);
-		GTK_IMHTML(imhtml)->tip_timer = g_timeout_add (TOOLTIP_TIMEOUT, 
+		if (!GTK_IMHTML(imhtml)->editable)
+			gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->hand_cursor);
+		GTK_IMHTML(imhtml)->tip_timer = g_timeout_add (TOOLTIP_TIMEOUT,
 							       gtk_imhtml_tip, imhtml);
 	}
-	
+
 	GTK_IMHTML(imhtml)->tip = tip;
 	g_slist_free(tags);
 	return FALSE;
@@ -298,7 +320,10 @@
 		g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
 		GTK_IMHTML(imhtml)->tip_timer = 0;
 	}
-	gdk_window_set_cursor(event->window, GTK_IMHTML(imhtml)->arrow_cursor);
+	if (GTK_IMHTML(imhtml)->editable)
+		gdk_window_set_cursor(event->window, GTK_IMHTML(imhtml)->text_cursor);
+	else
+		gdk_window_set_cursor(event->window, GTK_IMHTML(imhtml)->arrow_cursor);
 
 	/* propogate the event normally */
 	return FALSE;
@@ -307,11 +332,11 @@
 /*
  * XXX - This should be removed eventually.
  *
- * This function exists to work around a gross bug in GtkTextView.  
- * Basically, we short circuit ctrl+a and ctrl+end because they make 
+ * This function exists to work around a gross bug in GtkTextView.
+ * Basically, we short circuit ctrl+a and ctrl+end because they make
  * el program go boom.
  *
- * It's supposed to be fixed in gtk2.2.  You can view the bug report at 
+ * It's supposed to be fixed in gtk2.2.  You can view the bug report at
  * http://bugzilla.gnome.org/show_bug.cgi?id=107939
  */
 gboolean gtk_key_pressed_cb(GtkWidget *imhtml, GdkEventKey *event, gpointer data)
@@ -335,62 +360,107 @@
 }
 
 #if GTK_CHECK_VERSION(2,2,0)
-static GtkIMHtmlCopyable *gtk_imhtml_copyable_new(GtkIMHtml *imhtml, GtkTextMark *mark, const gchar *text) 
+static void gtk_imhtml_clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, GtkIMHtml *imhtml) {
+	GtkTextIter start, end;
+	GtkTextMark *sel = gtk_text_buffer_get_selection_bound(imhtml->text_buffer);
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	char *text;
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, sel);
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &end, ins);
+
+
+	if (info == TARGET_HTML) {
+		int len;
+		GString *str = g_string_new(NULL);
+		text = gtk_imhtml_get_markup_range(imhtml, &start, &end);
+
+		/* Mozilla asks that we start our text/html with the Unicode byte order mark */
+		str = g_string_append_unichar(str, 0xfeff);
+		str = g_string_append(str, text);
+		str = g_string_append_unichar(str, 0x0000);
+		char *selection = g_convert(str->str, str->len, "UCS-2", "UTF-8", NULL, &len, NULL);
+		gtk_selection_data_set (selection_data, gdk_atom_intern("text/html", FALSE), 16, selection, len);
+		g_string_free(str, TRUE);
+		g_free(selection);
+	} else {
+		text = gtk_text_buffer_get_text(imhtml->text_buffer, &start, &end, FALSE);
+		gtk_selection_data_set_text(selection_data, text, strlen(text));
+	}
+	g_free(text);
+}
+
+static void gtk_imhtml_primary_clipboard_clear(GtkClipboard *clipboard, GtkIMHtml *imhtml)
 {
-	GtkIMHtmlCopyable *copy = g_malloc(sizeof(GtkIMHtmlCopyable));
-	copy->mark = mark;
-	copy->text = g_strdup(text);
-	imhtml->copyables = g_slist_append(imhtml->copyables, copy);
-	return copy;
+	GtkTextIter insert;
+	GtkTextIter selection_bound;
+
+	gtk_text_buffer_get_iter_at_mark (imhtml->text_buffer, &insert,
+					  gtk_text_buffer_get_mark (imhtml->text_buffer, "insert"));
+	gtk_text_buffer_get_iter_at_mark (imhtml->text_buffer, &selection_bound,
+					  gtk_text_buffer_get_mark (imhtml->text_buffer, "selection_bound"));
+
+	if (!gtk_text_iter_equal (&insert, &selection_bound))
+		gtk_text_buffer_move_mark (imhtml->text_buffer,
+					   gtk_text_buffer_get_mark (imhtml->text_buffer, "selection_bound"),
+					   &insert);
 }
 
 static void copy_clipboard_cb(GtkIMHtml *imhtml, GtkClipboard *clipboard)
 {
-	GtkTextMark *sel = gtk_text_buffer_get_selection_bound(imhtml->text_buffer);
-	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
-	GtkTextIter start, end, smiley, last;
-	GString *str = g_string_new(NULL);
-	char *text;
+	gtk_clipboard_set_with_owner(gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD),
+				     selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry),
+				     (GtkClipboardGetFunc)gtk_imhtml_clipboard_get,
+				     (GtkClipboardClearFunc)NULL, G_OBJECT(imhtml));
 
-	GSList *copyables;
-	
-	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, sel);
-	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &end, ins);
-	
-	if (gtk_text_iter_equal(&start, &end))
-		return;
-	
-	gtk_text_iter_order(&start, &end);
-	last = start;
+	g_signal_stop_emission_by_name(imhtml, "copy-clipboard");
+}
+
+static void paste_received_cb (GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data)
+{
+	char *text;
+	guint16 c;
+	GtkIMHtml *imhtml = data;
 
-	for (copyables = imhtml->copyables; copyables != NULL; copyables = copyables->next) {
-		GtkIMHtmlCopyable *copy = GTK_IMHTML_COPYABLE(copyables->data);
-		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &smiley, copy->mark);
-		if (gtk_text_iter_compare(&end, &smiley) < 0) {
-			break;
-		}
-		if (gtk_text_iter_compare(&last, &smiley) <= 0) {
-			text = gtk_text_buffer_get_text(imhtml->text_buffer, &last, &smiley, FALSE);
-			str = g_string_append(str, text);
-			str = g_string_append(str, copy->text);
-			last = smiley;
-			g_free(text);
-		}
+	if (selection_data->length < 0) {
+		text = gtk_clipboard_wait_for_text(clipboard);
+	} else {
+		text = g_malloc((selection_data->format / 8) * selection_data->length);
+		memcpy(text, selection_data->data, selection_data->length * (selection_data->format / 8));
 	}
-	text = gtk_text_buffer_get_text(imhtml->text_buffer, &last, &end, FALSE);
-	str = g_string_append(str, text);
-	g_free(text);
-	
-	if (!gtk_text_iter_equal(&start, &last))
-		gtk_clipboard_set_text(clipboard ? clipboard :  
-				       gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD), 
-				       str->str, str->len);
-	g_string_free(str, TRUE);
+
+	memcpy (&c, text, 2);
+	if (c == 0xfeff) {
+		/* This is UCS2 */
+		char *utf8 = g_convert(text+2, (selection_data->length * (selection_data->format / 8)) - 2, "UTF-8", "UCS-2", NULL, NULL, NULL);
+		g_free(text);
+		text = utf8;
+	}
+	gtk_imhtml_close_tags(imhtml);
+	gtk_imhtml_append_text_with_images(imhtml, text, GTK_IMHTML_NO_NEWLINE, NULL);
+}
+
+
+static void paste_clipboard_cb(GtkIMHtml *imhtml, gpointer blah)
+{
+
+	GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
+	gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE),
+				       paste_received_cb, imhtml);
+	g_signal_stop_emission_by_name(imhtml, "paste-clipboard");
 }
 
 static gboolean button_release_cb(GtkIMHtml *imhtml, GdkEventButton event, gpointer the_foibles_of_man)
 {
-	copy_clipboard_cb(imhtml, gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY));
+	GtkClipboard *clipboard;
+	if (event.button == 1) {
+		if ((clipboard = gtk_widget_get_clipboard (GTK_WIDGET (imhtml),
+							   GDK_SELECTION_PRIMARY)))
+			gtk_text_buffer_remove_selection_clipboard (imhtml->text_buffer, clipboard);
+		gtk_clipboard_set_with_owner(gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY),
+					     selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry),
+					     (GtkClipboardGetFunc)gtk_imhtml_clipboard_get,
+					     (GtkClipboardClearFunc)gtk_imhtml_primary_clipboard_clear, G_OBJECT(imhtml));
+	}
 	return FALSE;
 }
 #endif
@@ -410,14 +480,12 @@
 {
 	GtkIMHtml *imhtml = GTK_IMHTML(object);
 	GList *scalables;
-#if GTK_CHECK_VERSION(2,2,0)
-	GSList *copyables;
-#endif
-	
+
 	g_hash_table_destroy(imhtml->smiley_data);
 	gtk_smiley_tree_destroy(imhtml->default_smilies);
 	gdk_cursor_unref(imhtml->hand_cursor);
 	gdk_cursor_unref(imhtml->arrow_cursor);
+	gdk_cursor_unref(imhtml->text_cursor);
 	if(imhtml->tip_window){
 		gtk_widget_destroy(imhtml->tip_window);
 	}
@@ -429,13 +497,6 @@
 		scale->free(scale);
 	}
 
-#if GTK_CHECK_VERSION(2,2,0)
-	for (copyables = imhtml->copyables; copyables; copyables = copyables->next) {
-		GtkIMHtmlCopyable *copy = GTK_IMHTML_COPYABLE(copyables->data);
-		g_free(copy->text);
-		g_free(copy);
-	}
-#endif
 	g_list_free(imhtml->scalables);
 	G_OBJECT_CLASS(parent_class)->finalize (object);
 }
@@ -468,13 +529,12 @@
 	imhtml->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, FALSE);
 	gtk_text_view_set_buffer(GTK_TEXT_VIEW(imhtml), imhtml->text_buffer);
 	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(imhtml), GTK_WRAP_WORD_CHAR);
-	gtk_text_view_set_editable(GTK_TEXT_VIEW(imhtml), FALSE);
 	gtk_text_view_set_pixels_below_lines(GTK_TEXT_VIEW(imhtml), 5);
-	gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(imhtml), FALSE);
+	/*gtk_text_view_set_indent(GTK_TEXT_VIEW(imhtml), -15);*/
 	/*gtk_text_view_set_justification(GTK_TEXT_VIEW(imhtml), GTK_JUSTIFY_FILL);*/
-	
+
 	/* These tags will be used often and can be reused--we create them on init and then apply them by name
-	 * other tags (color, size, face, etc.) will have to be created and applied dynamically */ 
+	 * other tags (color, size, face, etc.) will have to be created and applied dynamically */
 	gtk_text_buffer_create_tag(imhtml->text_buffer, "BOLD", "weight", PANGO_WEIGHT_BOLD, NULL);
 	gtk_text_buffer_create_tag(imhtml->text_buffer, "ITALICS", "style", PANGO_STYLE_ITALIC, NULL);
 	gtk_text_buffer_create_tag(imhtml->text_buffer, "UNDERLINE", "underline", PANGO_UNDERLINE_SINGLE, NULL);
@@ -483,10 +543,11 @@
 	gtk_text_buffer_create_tag(imhtml->text_buffer, "SUP", "rise", 5000, NULL);
 	gtk_text_buffer_create_tag(imhtml->text_buffer, "PRE", "family", "Monospace", NULL);
 	gtk_text_buffer_create_tag(imhtml->text_buffer, "search", "background", "#22ff00", "weight", "bold", NULL);
-
+	gtk_text_buffer_create_tag(imhtml->text_buffer, "LINK", "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL);
 	/* When hovering over a link, we show the hand cursor--elsewhere we show the plain ol' pointer cursor */
 	imhtml->hand_cursor = gdk_cursor_new (GDK_HAND2);
 	imhtml->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR);
+	imhtml->text_cursor = gdk_cursor_new (GDK_XTERM);
 
 	imhtml->show_smileys = TRUE;
 	imhtml->show_comments = TRUE;
@@ -499,8 +560,10 @@
 	g_signal_connect(G_OBJECT(imhtml), "motion-notify-event", G_CALLBACK(gtk_motion_event_notify), NULL);
 	g_signal_connect(G_OBJECT(imhtml), "leave-notify-event", G_CALLBACK(gtk_leave_event_notify), NULL);
 	g_signal_connect(G_OBJECT(imhtml), "key_press_event", G_CALLBACK(gtk_key_pressed_cb), NULL);
+	g_signal_connect_after(G_OBJECT(imhtml->text_buffer), "insert-text", G_CALLBACK(insert_cb), imhtml);
 #if GTK_CHECK_VERSION(2,2,0)
 	g_signal_connect(G_OBJECT(imhtml), "copy-clipboard", G_CALLBACK(copy_clipboard_cb), NULL);
+	g_signal_connect(G_OBJECT(imhtml), "paste-clipboard", G_CALLBACK(paste_clipboard_cb), NULL);
 	g_signal_connect(G_OBJECT(imhtml), "button-release-event", G_CALLBACK(button_release_cb), imhtml);
 #endif
 	gtk_widget_add_events(GTK_WIDGET(imhtml), GDK_LEAVE_NOTIFY_MASK);
@@ -509,10 +572,21 @@
 	imhtml->tip_timer = 0;
 	imhtml->tip_window = NULL;
 
+	imhtml->edit.bold = NULL;
+	imhtml->edit.italic = NULL;
+	imhtml->edit.underline = NULL;
+	imhtml->edit.forecolor = NULL;
+	imhtml->edit.backcolor = NULL;
+	imhtml->edit.fontface = NULL;
+	imhtml->edit.sizespan = NULL;
+	imhtml->edit.fontsize = 3;
+
+	imhtml->format_spans = NULL;
+
 	imhtml->scalables = NULL;
-#if GTK_CHECK_VERSION(2,2,0)
-	imhtml->copyables = NULL;
-#endif
+
+	gtk_imhtml_set_editable(imhtml, FALSE);
+
 }
 
 GtkWidget *gtk_imhtml_new(void *a, void *b)
@@ -551,9 +625,8 @@
 
 static void url_open(GtkWidget *w, struct url_data *data) {
 	if(!data) return;
+	g_signal_emit(data->object, signals[URL_CLICKED], 0, data->url);
 
-	g_signal_emit(data->object, signals[URL_CLICKED], 0, data->url);
-	
 	g_object_unref(data->object);
 	g_free(data->url);
 	g_free(data);
@@ -572,9 +645,10 @@
 /* The callback for an event on a link tag. */
 gboolean tag_event(GtkTextTag *tag, GObject *imhtml, GdkEvent *event, GtkTextIter *arg2, char *url) {
 	GdkEventButton *event_button = (GdkEventButton *) event;
-
+	if (GTK_IMHTML(imhtml)->editable)
+		return FALSE;
 	if (event->type == GDK_BUTTON_RELEASE) {
-		if (event_button->button == 1) { 
+		if (event_button->button == 1) {
 			GtkTextIter start, end;
 			/* we shouldn't open a URL if the user has selected something: */
 			gtk_text_buffer_get_selection_bounds(
@@ -602,7 +676,10 @@
 				g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
 				GTK_IMHTML(imhtml)->tip_timer = 0;
 			}
-			gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor);
+			if (GTK_IMHTML(imhtml)->editable)
+				gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->text_cursor);
+			else
+				gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor);
 			menu = gtk_menu_new();
 
 			/* buttons and such */
@@ -895,9 +972,11 @@
 		   gint        *len,
 		   gint        *type)
 {
+	char *close;
 	*type = 1;
 
-	if (!strchr (string, '>'))
+	
+	if (!(close = strchr (string, '>')))
 		return FALSE;
 
 	VALID_TAG ("B");
@@ -963,6 +1042,7 @@
 	VALID_TAG ("BR/"); /* hack until gtkimhtml handles things better */
 	VALID_TAG ("IMG");
 	VALID_TAG("SPAN");
+	VALID_OPT_TAG("BR");
 
 	if (!g_ascii_strncasecmp(string, "!--", strlen ("!--"))) {
 		gchar *e = strstr (string + strlen("!--"), "-->");
@@ -971,9 +1051,12 @@
 			*tag = g_strndup (string + strlen ("!--"), *len - strlen ("!---->"));
 			return TRUE;
 		}
-	}
-
-	return FALSE;
+	} 
+	
+	*type = -1;
+	*len = close - string + 1;
+	*tag = g_strndup(string, *len - 1);
+	return TRUE;
 }
 
 static gchar*
@@ -1036,85 +1119,18 @@
 }
 
 
-
-#define NEW_TEXT_BIT 0
-#define NEW_COMMENT_BIT 2
-#define NEW_SCALABLE_BIT 1
-#define NEW_BIT(x)	ws [wpos] = '\0'; \
-                        mark2 = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); \
-                        gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, -1); \
-						gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \
-                        gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &siter, mark2); \
-                        gtk_text_buffer_delete_mark(imhtml->text_buffer, mark2); \
-                        if (bold) \
-                                 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "BOLD", &siter, &iter); \
-						if (italics) \
-                                 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "ITALICS", &siter, &iter); \
-                        if (underline) \
-                                 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "UNDERLINE", &siter, &iter); \
-                        if (strike) \
-                                 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "STRIKE", &siter, &iter); \
-                        if (sub) \
-                                 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "SUB", &siter, &iter); \
-                        if (sup) \
-                                 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "SUP", &siter, &iter); \
-                        if (pre) \
-                                 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "PRE", &siter, &iter); \
-                        if (bg) { \
-                                texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", bg, NULL); \
-                                gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \
-                        } \
-                        if (fonts) { \
-                                 GtkIMHtmlFontDetail *fd = fonts->data; \
-                                 if (fd->fore) { \
-	                                    texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "foreground", fd->fore, NULL); \
-                                            gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \
-                                 } \
-                                 if (fd->back) { \
-                                            texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", fd->back, NULL); \
-                                            gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \
-                                 } \
-                                 if (fd->face) { \
-                                            texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "family", fd->face, NULL); \
-                                            gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \
-                                 } \
-                                 if (fd->size) { \
-                                            texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "size-points", (double)POINT_SIZE(fd->size), NULL); \
-                                            gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \
-                                 } \
-                        } \
-                        if (url) { \
-                                 texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL); \
-                                 g_signal_connect(G_OBJECT(texttag), "event", G_CALLBACK(tag_event), g_strdup(url)); \
-                                 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \
-                                 texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, NULL); \
-                                 g_object_set_data(G_OBJECT(texttag), "link_url", g_strdup(url)); \
-                                 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \
-                        } \
-                        wpos = 0; \
-                        ws[0] = 0; \
-                        gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \
-                        if (x == NEW_SCALABLE_BIT) { \
-								GdkRectangle rect; \
-								gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect); \
-								scalable->add_to(scalable, imhtml, &iter); \
-								scalable->scale(scalable, rect.width, rect.height); \
-								imhtml->scalables = g_list_append(imhtml->scalables, scalable); \
-								gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \
-                        } \
-
-
-
 GString* gtk_imhtml_append_text_with_images (GtkIMHtml        *imhtml,
 					     const gchar      *text,
 					     GtkIMHtmlOptions  options,
 					     GSList           *images)
 {
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter insert;
+	GdkRectangle rect;
 	gint pos = 0;
 	GString *str = NULL;
-	GtkTextIter iter, siter;
-	GtkTextMark *mark, *mark2;
-	GtkTextTag *texttag;
+	GtkTextIter iter;
+	GtkTextMark *mark;
 	gchar *ws;
 	gchar *tag;
 	gchar *url = NULL;
@@ -1132,19 +1148,17 @@
 		sub = 0,
 		sup = 0,
 		title = 0,
-		pre = 0;	
+		pre = 0;
 
 	GSList *fonts = NULL;
-
-	GdkRectangle rect;
+	GtkIMHtmlScalable *scalable = NULL;
 	int y, height;
 
-	GtkIMHtmlScalable *scalable = NULL;
 
 	g_return_val_if_fail (imhtml != NULL, NULL);
 	g_return_val_if_fail (GTK_IS_IMHTML (imhtml), NULL);
 	g_return_val_if_fail (text != NULL, NULL);
-
+	printf("Appending: %s\n", text);
 	c = text;
 	len = strlen(text);
 	ws = g_malloc(len + 1);
@@ -1153,13 +1167,22 @@
 	if (options & GTK_IMHTML_RETURN_LOG)
 		str = g_string_new("");
 
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &insert, ins);
+
 	gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter);
 	mark = gtk_text_buffer_create_mark (imhtml->text_buffer, NULL, &iter, /* right grav */ FALSE);
 
-	gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);	
+	gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
 	gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(imhtml), &iter, &y, &height);
 
-	if(((y + height) - (rect.y + rect.height)) > height 
+#if GTK_CHECK_VERSION(2,2,0)
+	gtk_imhtml_primary_clipboard_clear(NULL, imhtml);
+#endif
+	gtk_text_buffer_move_mark (imhtml->text_buffer,
+				   gtk_text_buffer_get_mark (imhtml->text_buffer, "insert"),
+				   &iter);
+
+	if(((y + height) - (rect.y + rect.height)) > height
 	   && gtk_text_buffer_get_char_count(imhtml->text_buffer)){
 		options |= GTK_IMHTML_NO_SCROLL;
 	}
@@ -1168,85 +1191,123 @@
 		if (*c == '<' && gtk_imhtml_is_tag (c + 1, &tag, &tlen, &type)) {
 			c++;
 			pos++;
-			switch (type) 
+			ws[wpos] = '\0';
+			switch (type)
 				{
 				case 1:		/* B */
 				case 2:		/* BOLD */
 				case 54:	/* STRONG */
-					NEW_BIT (NEW_TEXT_BIT);
+					if (url)
+						gtk_imhtml_insert_link(imhtml, url, ws);
+					else
+						gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+					if (bold == 0)
+						gtk_imhtml_toggle_bold(imhtml);
 					bold++;
+					ws[0] = '\0'; wpos = 0;
 					break;
 				case 3:		/* /B */
 				case 4:		/* /BOLD */
 				case 55:	/* /STRONG */
-					NEW_BIT (NEW_TEXT_BIT);
+					if (url)
+						gtk_imhtml_insert_link(imhtml, url, ws);
+					else
+						gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+					ws[0] = '\0'; wpos = 0;
+
 					if (bold)
 						bold--;
+					if (bold == 0)
+						gtk_imhtml_toggle_bold(imhtml);
 					break;
 				case 5:		/* I */
 				case 6:		/* ITALIC */
 				case 52:	/* EM */
-					NEW_BIT (NEW_TEXT_BIT);
+					if (url)
+						gtk_imhtml_insert_link(imhtml, url, ws);
+					else
+						gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+					ws[0] = '\0'; wpos = 0;
+					if (italics == 0)
+						gtk_imhtml_toggle_italic(imhtml);
 					italics++;
 					break;
 				case 7:		/* /I */
 				case 8:		/* /ITALIC */
 				case 53:	/* /EM */
-					NEW_BIT (NEW_TEXT_BIT);
+					if (url)
+						gtk_imhtml_insert_link(imhtml, url, ws);
+					else
+						gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+					ws[0] = '\0'; wpos = 0;
 					if (italics)
 						italics--;
+					if (italics == 0)
+						gtk_imhtml_toggle_italic(imhtml);
 					break;
 				case 9:		/* U */
 				case 10:	/* UNDERLINE */
-					NEW_BIT (NEW_TEXT_BIT);
+					if (url)
+						gtk_imhtml_insert_link(imhtml, url, ws);
+					else
+						gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+					ws[0] = '\0'; wpos = 0;
+					if (underline == 0)
+						gtk_imhtml_toggle_underline(imhtml);
 					underline++;
 					break;
 				case 11:	/* /U */
 				case 12:	/* /UNDERLINE */
-					NEW_BIT (NEW_TEXT_BIT);
+					if (url)
+						gtk_imhtml_insert_link(imhtml, url, ws);
+					else
+						gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+					ws[0] = '\0'; wpos = 0;
 					if (underline)
 						underline--;
+					if (underline == 0)
+						gtk_imhtml_toggle_underline(imhtml);
 					break;
 				case 13:	/* S */
 				case 14:	/* STRIKE */
-					NEW_BIT (NEW_TEXT_BIT);
+					//NEW_BIT (NEW_TEXT_BIT);
 					strike++;
 					break;
 				case 15:	/* /S */
 				case 16:	/* /STRIKE */
-					NEW_BIT (NEW_TEXT_BIT);
+					//NEW_BIT (NEW_TEXT_BIT);
 					if (strike)
 						strike--;
 					break;
 				case 17:	/* SUB */
-					NEW_BIT (NEW_TEXT_BIT);
+					//NEW_BIT (NEW_TEXT_BIT);
 					sub++;
 					break;
 				case 18:	/* /SUB */
-					NEW_BIT (NEW_TEXT_BIT);
+					//NEW_BIT (NEW_TEXT_BIT);
 					if (sub)
 						sub--;
 					break;
 				case 19:	/* SUP */
-					NEW_BIT (NEW_TEXT_BIT);
+					//NEW_BIT (NEW_TEXT_BIT);
 					sup++;
 				break;
 				case 20:	/* /SUP */
-					NEW_BIT (NEW_TEXT_BIT);
+					//NEW_BIT (NEW_TEXT_BIT);
 					if (sup)
 						sup--;
 					break;
 				case 21:	/* PRE */
-					NEW_BIT (NEW_TEXT_BIT);
+					//NEW_BIT (NEW_TEXT_BIT);
 					pre++;
 					break;
 				case 22:	/* /PRE */
-					NEW_BIT (NEW_TEXT_BIT);
+					//NEW_BIT (NEW_TEXT_BIT);
 					if (pre)
 						pre--;
 					break;
 				case 23:	/* TITLE */
-					NEW_BIT (NEW_TEXT_BIT);
+					//NEW_BIT (NEW_TEXT_BIT);
 					title++;
 					break;
 				case 24:	/* /TITLE */
@@ -1260,28 +1321,49 @@
 					break;
 				case 25:	/* BR */
 				case 58:	/* BR/ */
+				case 61:	/* BR (opt) */
 					ws[wpos] = '\n';
 					wpos++;
-					NEW_BIT (NEW_TEXT_BIT);
+					//NEW_BIT (NEW_TEXT_BIT);
 					break;
 				case 26:        /* HR */
 				case 42:        /* HR (opt) */
 					ws[wpos++] = '\n';
+					if (url)
+						gtk_imhtml_insert_link(imhtml, url, ws);
+					else
+						gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
 					scalable = gtk_imhtml_hr_new();
-					NEW_BIT(NEW_SCALABLE_BIT);
+					gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
+					scalable->add_to(scalable, imhtml, &iter);
+					scalable->scale(scalable, rect.width, rect.height);
+					imhtml->scalables = g_list_append(imhtml->scalables, scalable);
+					ws[0] = '\0'; wpos = 0;
 					ws[wpos++] = '\n';
+
 					break;
 				case 27:	/* /FONT */
 					if (fonts) {
 						GtkIMHtmlFontDetail *font = fonts->data;
-						NEW_BIT (NEW_TEXT_BIT);
+						if (url)
+							gtk_imhtml_insert_link(imhtml, url, ws);
+						else
+							gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+						ws[0] = '\0'; wpos = 0;
+						//NEW_BIT (NEW_TEXT_BIT);
 						fonts = g_slist_remove (fonts, font);
-						if (font->face)
+						if (font->face) {
+							gtk_imhtml_toggle_fontface(imhtml, NULL);
 							g_free (font->face);
-						if (font->fore)
+						}
+						if (font->fore) {
+							gtk_imhtml_toggle_forecolor(imhtml, NULL);
 							g_free (font->fore);
-						if (font->back)
+						}
+						if (font->back) {
+							gtk_imhtml_toggle_backcolor(imhtml, NULL);
 							g_free (font->back);
+						}
 						if (font->sml)
 							g_free (font->sml);
 						g_free (font);
@@ -1289,11 +1371,15 @@
 					break;
 				case 28:        /* /A    */
 					if (url) {
-						NEW_BIT(NEW_TEXT_BIT);
+						gtk_imhtml_insert_link(imhtml, url, ws);
 						g_free(url);
+						ws[0] = '\0'; wpos = 0;
 						url = NULL;
-						break;
+						ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+						gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
 					}
+					break;
+					
 				case 29:	/* P */
 				case 30:	/* /P */
 				case 31:	/* H3 */
@@ -1319,9 +1405,13 @@
 						sml = gtk_imhtml_get_html_opt (tag, "SML=");
 						if (!(color || back || face || size || sml))
 							break;
-						
-						NEW_BIT (NEW_TEXT_BIT);
-						
+
+						if (url)
+							gtk_imhtml_insert_link(imhtml, url, ws);
+						else
+							gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+						ws[0] = '\0'; wpos = 0;
+
 						font = g_new0 (GtkIMHtmlFontDetail, 1);
 						if (fonts)
 							oldfont = fonts->data;
@@ -1330,20 +1420,22 @@
 							font->fore = color;
 						else if (oldfont && oldfont->fore)
 							font->fore = g_strdup(oldfont->fore);
+						if (font->fore)
+							gtk_imhtml_toggle_forecolor(imhtml, font->fore);
 
 						if (back && !(options & GTK_IMHTML_NO_COLOURS))
 							font->back = back;
 						else if (oldfont && oldfont->back)
 							font->back = g_strdup(oldfont->back);
-						
+						if (font->back)
+							gtk_imhtml_toggle_backcolor(imhtml, font->back);
+
 						if (face && !(options & GTK_IMHTML_NO_FONTS))
 							font->face = face;
 						else if (oldfont && oldfont->face)
 							font->face = g_strdup(oldfont->face);
-						if (font->face && (atoi(font->face) > 100)) {
-							g_free(font->face);
-							font->face = g_strdup("100");
-						}
+						if (font->face)
+							gtk_imhtml_toggle_fontface(imhtml, font->face);
 
 						if (sml)
 							font->sml = sml;
@@ -1359,11 +1451,12 @@
 								font->size = MAX (0, 3 - font->size);
 							} else if (isdigit (*size)) {
 								sscanf (size, "%hd", &font->size);
-							}
+					}
 							if (font->size > 100)
 								font->size = 100;
 						} else if (oldfont)
 							font->size = oldfont->size;
+						//	gtk_imhtml_font_set_size(imhtml, font->size);
 						g_free(size);
 						fonts = g_slist_prepend (fonts, font);
 					}
@@ -1372,10 +1465,16 @@
 					if (!(options & GTK_IMHTML_NO_COLOURS)) {
 						char *bgcolor = gtk_imhtml_get_html_opt (tag, "BGCOLOR=");
 						if (bgcolor) {
-							NEW_BIT(NEW_TEXT_BIT);
+							if (url)
+								gtk_imhtml_insert_link(imhtml, url, ws);
+							else
+								gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+							ws[0] = '\0'; wpos = 0;
+							//		NEW_BIT(NEW_TEXT_BIT);
 							if (bg)
 								g_free(bg);
 							bg = bgcolor;
+							gtk_imhtml_toggle_backcolor(imhtml, bg);
 						}
 					}
 					break;
@@ -1383,9 +1482,12 @@
 					{
 						gchar *href = gtk_imhtml_get_html_opt (tag, "HREF=");
 						if (href) {
-							NEW_BIT (NEW_TEXT_BIT);
-							if (url)
-								g_free (url);
+							if (url) {
+								gtk_imhtml_insert_link(imhtml, url, ws);
+								g_free(url);
+							} else
+								gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+							ws[0] = '\0'; wpos = 0;
 							url = href;
 						}
 					}
@@ -1408,7 +1510,7 @@
 						}
 
 						scalable = gtk_imhtml_image_new(img, filename);
-						NEW_BIT(NEW_SCALABLE_BIT);
+						//NEW_BIT(NEW_SCALABLE_BIT);
 						g_object_unref(G_OBJECT(img));
 					}
 				case 47:	/* P (opt) */
@@ -1420,11 +1522,11 @@
 				case 57:	/* /SPAN */
 				case 60:    /* SPAN */
 					break;
-				case 61:	/* comment */
-					NEW_BIT (NEW_TEXT_BIT);
+				case 62:	/* comment */
+					//NEW_BIT (NEW_TEXT_BIT);
 					if (imhtml->show_comments)
 						wpos = g_snprintf (ws, len, "%s", tag);
-					NEW_BIT (NEW_COMMENT_BIT);
+					//  NEW_BIT (NEW_COMMENT_BIT);
 					break;
 				default:
 					break;
@@ -1443,79 +1545,60 @@
 			if (!(options & GTK_IMHTML_NO_NEWLINE)) {
 				ws[wpos] = '\n';
 				wpos++;
-				NEW_BIT (NEW_TEXT_BIT);
+				if (url)
+					gtk_imhtml_insert_link(imhtml, url, ws);
+				else
+					gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+				ws[0] = '\0';
+				wpos = 0;
+				//NEW_BIT (NEW_TEXT_BIT);
 			}
 			c++;
 			pos++;
 		} else if (imhtml->show_smileys && (gtk_imhtml_is_smiley (imhtml, fonts, c, &smilelen) || gtk_imhtml_is_smiley(imhtml, NULL, c, &smilelen))) {
-			GtkTextChildAnchor *anchor;
-			GtkWidget *icon = NULL;
-			GtkTextIter copy;
-			GdkPixbufAnimation *annipixbuf = NULL;
-			GdkPixbuf *pixbuf = NULL;
 			GtkIMHtmlFontDetail *fd;
-			
+
 			gchar *sml = NULL;
 			if (fonts) {
 				fd = fonts->data;
 				sml = fd->sml;
 			}
-			NEW_BIT (NEW_TEXT_BIT);
+			if (url)
+				gtk_imhtml_insert_link(imhtml, url, ws);
+			else {
+				printf("Inserting %s\n", ws);
+				gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+			}
 			wpos = g_snprintf (ws, smilelen + 1, "%s", c);
-			anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, &iter);
-			annipixbuf = gtk_smiley_tree_image(imhtml, sml, ws);
-			if(annipixbuf) {
-				if(gdk_pixbuf_animation_is_static_image(annipixbuf)) {
-					pixbuf = gdk_pixbuf_animation_get_static_image(annipixbuf);
-					if(pixbuf)
-						icon = gtk_image_new_from_pixbuf(pixbuf);
-				} else {
-					icon = gtk_image_new_from_animation(annipixbuf);
-				}
-			}
+			gtk_imhtml_insert_smiley(imhtml, sml, ws);
 
-			if (icon) {
-				gtk_widget_show(icon);
-				gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), icon, anchor);
-#if GTK_CHECK_VERSION(2,2,0)
-				gtk_imhtml_copyable_new(imhtml, 
-							gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE), 
-							ws);
-#endif
-			}
-			
-			copy = iter;
-			gtk_text_iter_backward_char(&copy);
-			if (bg) {					
-                                texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", bg, NULL); 
-                                gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &iter, &copy); 
-                        } 
-                        if (fonts) { 
-                                 GtkIMHtmlFontDetail *fd = fonts->data; 
-				 if (fd->back) { 
-					 texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", fd->back, NULL); 
-					 gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &iter, &copy); 
-                                 }
-			} 
+			ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+			gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
+
 			c += smilelen;
 			pos += smilelen;
 			wpos = 0;
 			ws[0] = 0;
-	} else if (*c) {
+		} else if (*c) {
 			ws [wpos++] = *c++;
 			pos++;
 		} else {
 			break;
 		}
 	}
-	
-	NEW_BIT(NEW_TEXT_BIT);
+	if (url)
+		gtk_imhtml_insert_link(imhtml, url, ws);
+	else
+		gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos);
+	ws[0] = '\0'; wpos = 0;
+
+	//NEW_BIT(NEW_TEXT_BIT);
 	if (url) {
 		g_free (url);
 		if (str)
 			str = g_string_append (str, "</A>");
 	}
-	
+
 	while (fonts) {
 		GtkIMHtmlFontDetail *font = fonts->data;
 		fonts = g_slist_remove (fonts, font);
@@ -1569,10 +1652,14 @@
 	g_free (ws);
 	if(bg)
 		g_free(bg);
+	gtk_imhtml_close_tags(imhtml);
 	if (!(options & GTK_IMHTML_NO_SCROLL))
 		gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (imhtml), mark,
 					      0, TRUE, 0.0, 1.0);
 	gtk_text_buffer_delete_mark (imhtml->text_buffer, mark);
+	gtk_text_buffer_move_mark (imhtml->text_buffer,
+				   gtk_text_buffer_get_mark (imhtml->text_buffer, "insert"),
+				   &iter);
 	return str;
 }
 
@@ -1606,12 +1693,32 @@
 	gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end);
 	gtk_text_buffer_delete(imhtml->text_buffer, &start, &end);
 
+	for(del = imhtml->format_spans; del; del = del->next) {
+		GtkIMHtmlFormatSpan *span = del->data;
+		if (span->start_tag)
+			g_free(span->start_tag);
+		if (span->end_tag)
+			g_free(span->end_tag);
+		g_free(span);
+	}
+	g_list_free(imhtml->format_spans);
+	imhtml->format_spans = NULL;
+
 	for(del = imhtml->scalables; del; del = del->next) {
 		GtkIMHtmlScalable *scale = del->data;
 		scale->free(scale);
 	}
 	g_list_free(imhtml->scalables);
 	imhtml->scalables = NULL;
+
+	imhtml->edit.bold = NULL;
+	imhtml->edit.italic = NULL;
+	imhtml->edit.underline = NULL;
+	imhtml->edit.fontface = NULL;
+	imhtml->edit.forecolor = NULL;
+	imhtml->edit.backcolor = NULL;
+	imhtml->edit.sizespan = NULL;
+	imhtml->edit.fontsize = 3;
 }
 
 void gtk_imhtml_page_up (GtkIMHtml *imhtml)
@@ -1623,7 +1730,7 @@
 	gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, rect.x,
 									   rect.y - rect.height);
 	gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &iter, 0, TRUE, 0, 0);
-	
+
 }
 void gtk_imhtml_page_down (GtkIMHtml *imhtml)
 {
@@ -1781,7 +1888,7 @@
 	g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(sel)->ok_button), "clicked",
 							 G_CALLBACK(gtk_widget_destroy), sel);
 	g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(sel)->cancel_button), "clicked",
-							 G_CALLBACK(gtk_widget_destroy), sel); 
+							 G_CALLBACK(gtk_widget_destroy), sel);
 
 	gtk_widget_show(sel);
 }
@@ -1867,7 +1974,7 @@
 {
 	GtkIMHtmlHr *hr = (GtkIMHtmlHr *)scale;
 	GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
-
+	g_object_set_data(G_OBJECT(anchor), "text_tag", "<hr>");
 	gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), hr->sep, anchor);
 }
 
@@ -1883,17 +1990,16 @@
 
 	g_return_val_if_fail(imhtml != NULL, FALSE);
 	g_return_val_if_fail(text != NULL, FALSE);
-	
+
 	if (imhtml->search_string && !strcmp(text, imhtml->search_string))
 		new_search = FALSE;
-	 
-	
+
 	if (new_search) {
 		gtk_imhtml_search_clear(imhtml);
 		gtk_text_buffer_get_start_iter(imhtml->text_buffer, &iter);
 	} else {
 		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter,
-						 gtk_text_buffer_get_mark(imhtml->text_buffer, "search"));	
+						 gtk_text_buffer_get_mark(imhtml->text_buffer, "search"));
 	}
 	imhtml->search_string = g_strdup(text);
 
@@ -1905,24 +2011,27 @@
 		gtk_text_buffer_create_mark(imhtml->text_buffer, "search", &end, FALSE);
 		if (new_search) {
 			gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "search", &iter, &end);
-			do 
+			do
 				gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "search", &start, &end);
-			while (gtk_source_iter_forward_search(&end, imhtml->search_string, 
-							      GTK_SOURCE_SEARCH_VISIBLE_ONLY | 
+			while (gtk_source_iter_forward_search(&end, imhtml->search_string,
+							      GTK_SOURCE_SEARCH_VISIBLE_ONLY |
 							      GTK_SOURCE_SEARCH_CASE_INSENSITIVE,
 							      &start, &end, NULL));
 		}
 		return TRUE;
 	}
+
+	gtk_imhtml_search_clear(imhtml);
+
 	return FALSE;
 }
 
 void gtk_imhtml_search_clear(GtkIMHtml *imhtml)
 {
 	GtkTextIter start, end;
-	
+
 	g_return_if_fail(imhtml != NULL);
-	
+
 	gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start);
 	gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end);
 
@@ -1931,3 +2040,546 @@
 		g_free(imhtml->search_string);
 	imhtml->search_string = NULL;
 }
+
+/* Editable stuff */
+static void insert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml)
+{
+	GtkIMHtmlFormatSpan *span = NULL;
+	GtkTextIter end;
+
+	gtk_text_iter_forward_chars(iter, len);
+	gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end);
+	gtk_text_iter_forward_char(&end);
+
+	if (!gtk_text_iter_equal(&end, iter))
+		return;
+
+
+	if ((span = imhtml->edit.bold)) {
+		GtkTextIter bold;
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &bold, span->start);
+		gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "BOLD", &bold, iter);
+	}
+
+	if ((span = imhtml->edit.italic)) {
+		GtkTextIter italic;
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &italic, span->start);
+		gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "ITALICS", &italic,
+				iter);
+	}
+
+	if ((span = imhtml->edit.underline)) {
+		GtkTextIter underline;
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &underline, span->start);
+		gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "UNDERLINE", &underline,
+				iter);
+	}
+
+	if ((span = imhtml->edit.forecolor)) {
+		GtkTextIter fore;
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &fore, span->start);
+		gtk_text_buffer_apply_tag(imhtml->text_buffer, span->tag, &fore, iter);
+	}
+
+	if ((span = imhtml->edit.backcolor)) {
+		GtkTextIter back;
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &back, span->start);
+		gtk_text_buffer_apply_tag(imhtml->text_buffer, span->tag, &back, iter);
+	}
+
+	if ((span = imhtml->edit.fontface)) {
+		GtkTextIter face;
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &face, span->start);
+		gtk_text_buffer_apply_tag(imhtml->text_buffer, span->tag, &face, iter);
+	}
+
+	if ((span = imhtml->edit.sizespan)) {
+		GtkTextIter size;
+		/* We create the tags here so that one can grow font or shrink font several times
+		 * in a row without creating unnecessary tags */
+		if (span->tag == NULL) {
+			span->tag = gtk_text_buffer_create_tag
+				(imhtml->text_buffer, NULL, "size-points", (double)_point_sizes [imhtml->edit.fontsize-1], NULL);
+			span->start_tag = g_strdup_printf("<font size=\"%d\">", imhtml->edit.fontsize);
+			span->end_tag = g_strdup("</font>");
+		}
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &size, span->start);
+		gtk_text_buffer_apply_tag(imhtml->text_buffer, span->tag, &size, iter);
+	}
+}
+
+void gtk_imhtml_set_editable(GtkIMHtml *imhtml, gboolean editable)
+{
+	gtk_text_view_set_editable(GTK_TEXT_VIEW(imhtml), editable);
+	gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(imhtml), editable);
+	imhtml->editable = editable;
+}
+
+gboolean gtk_imhtml_get_editable(GtkIMHtml *imhtml)
+{
+	return imhtml->editable;
+}
+
+gboolean gtk_imhtml_toggle_bold(GtkIMHtml *imhtml)
+{
+	GtkIMHtmlFormatSpan *span;
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter iter;
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
+	if (!imhtml->edit.bold) {
+		span = g_malloc(sizeof(GtkIMHtmlFormatSpan));
+		span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		span->start_tag = g_strdup("<b>");
+		span->end = NULL;
+		span->end_tag = g_strdup("</b>");
+		span->buffer = imhtml->text_buffer;
+		span->tag =  gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), "BOLD");
+		imhtml->edit.bold = span;
+		imhtml->format_spans = g_list_append(imhtml->format_spans, span);
+	} else {
+		span = imhtml->edit.bold;
+		span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		imhtml->edit.bold = NULL;
+	}
+	return imhtml->edit.bold != NULL;
+}
+
+gboolean gtk_imhtml_toggle_italic(GtkIMHtml *imhtml)
+{
+	GtkIMHtmlFormatSpan *span;
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter iter;
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
+	if (!imhtml->edit.italic) {
+		span = g_malloc(sizeof(GtkIMHtmlFormatSpan));
+		span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		span->start_tag = g_strdup("<i>");
+		span->end = NULL;
+		span->end_tag = g_strdup("</i>");
+		span->buffer = imhtml->text_buffer;
+		span->tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), "ITALIC");
+		imhtml->edit.italic = span;
+		imhtml->format_spans = g_list_append(imhtml->format_spans, span);
+	} else {
+		span = imhtml->edit.italic;
+		span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		imhtml->edit.italic = NULL;
+	}
+	return imhtml->edit.italic != NULL;
+}
+
+gboolean gtk_imhtml_toggle_underline(GtkIMHtml *imhtml)
+{
+	GtkIMHtmlFormatSpan *span;
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter iter;
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
+	if (!imhtml->edit.underline) {
+		span = g_malloc(sizeof(GtkIMHtmlFormatSpan));
+		span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		span->start_tag = g_strdup("<u>");
+		span->end = NULL;
+		span->end_tag = g_strdup("</u>");
+		span->buffer = imhtml->text_buffer;
+		span->tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), "UNDERLINE");
+		imhtml->edit.underline = span;
+		imhtml->format_spans = g_list_append(imhtml->format_spans, span);
+	} else {
+		span = imhtml->edit.underline;
+		span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		imhtml->edit.underline = NULL;
+	}
+	return imhtml->edit.underline != NULL;
+}
+
+void gtk_imhtml_font_set_size(GtkIMHtml *imhtml, gint size)
+{
+	GtkIMHtmlFormatSpan *span;
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter iter;
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
+
+	imhtml->edit.fontsize = size;
+
+	if (imhtml->edit.sizespan) {
+		GtkTextIter iter2;
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter2, imhtml->edit.sizespan->start);
+		if (gtk_text_iter_equal(&iter2, &iter))
+			return;
+		span = imhtml->edit.sizespan;
+		span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+	}
+	if (size != -1) {
+		span = g_malloc(sizeof(GtkIMHtmlFormatSpan));
+		span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		span->end = NULL;
+		span->buffer = imhtml->text_buffer;
+		span->tag = NULL;
+		imhtml->edit.sizespan = span;
+		imhtml->format_spans = g_list_append(imhtml->format_spans, span);
+	}
+}
+
+void gtk_imhtml_font_shrink(GtkIMHtml *imhtml)
+{
+	GtkIMHtmlFormatSpan *span;
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter iter;
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
+	if (imhtml->edit.fontsize == 1)
+		return;
+
+	imhtml->edit.fontsize--;
+
+	if (imhtml->edit.sizespan) {
+		GtkTextIter iter2;
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter2, imhtml->edit.sizespan->start);
+		if (gtk_text_iter_equal(&iter2, &iter))
+			return;
+		span = imhtml->edit.sizespan;
+		span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+	}
+
+	span = g_malloc(sizeof(GtkIMHtmlFormatSpan));
+	span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+	span->end = NULL;
+	span->buffer = imhtml->text_buffer;
+	span->tag = NULL;
+	imhtml->edit.sizespan = span;
+	imhtml->format_spans = g_list_append(imhtml->format_spans, span);
+}
+
+void gtk_imhtml_font_grow(GtkIMHtml *imhtml)
+{
+	GtkIMHtmlFormatSpan *span;
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter iter;
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
+	if (imhtml->edit.fontsize == MAX_FONT_SIZE)
+		return;
+
+	imhtml->edit.fontsize++;
+
+	if (imhtml->edit.sizespan) {
+		GtkTextIter iter2;
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter2, imhtml->edit.sizespan->start);
+		if (gtk_text_iter_equal(&iter2, &iter))
+			return;
+		span = imhtml->edit.sizespan;
+		span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+	}
+
+	span = g_malloc(sizeof(GtkIMHtmlFormatSpan));
+	span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+	span->end = NULL;
+	span->tag = NULL;
+	span->buffer = imhtml->text_buffer;
+	imhtml->edit.sizespan = span;
+	imhtml->format_spans = g_list_append(imhtml->format_spans, span);
+}
+
+gboolean gtk_imhtml_toggle_forecolor(GtkIMHtml *imhtml, const char *color)
+{
+	GtkIMHtmlFormatSpan *span;
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter iter;
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
+	if (!imhtml->edit.forecolor) {
+		span = g_malloc(sizeof(GtkIMHtmlFormatSpan));
+		span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		span->start_tag = g_strdup_printf("<font color=\"%s\">", color);
+		span->end = NULL;
+		span->end_tag = g_strdup("</font>");
+		span->buffer = imhtml->text_buffer;
+		span->tag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "foreground", color, NULL);
+		imhtml->edit.forecolor = span;
+		imhtml->format_spans = g_list_append(imhtml->format_spans, span);
+	} else {
+		span = imhtml->edit.forecolor;
+		span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		imhtml->edit.forecolor = NULL;
+	}
+
+
+	return imhtml->edit.forecolor != NULL;
+}
+
+gboolean gtk_imhtml_toggle_backcolor(GtkIMHtml *imhtml, const char *color)
+{
+	GtkIMHtmlFormatSpan *span;
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter iter;
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
+	if (!imhtml->edit.backcolor) {
+		span = g_malloc(sizeof(GtkIMHtmlFormatSpan));
+		span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		span->start_tag = g_strdup_printf("<font back=\"%s\">", color);
+		span->end = NULL;
+		span->end_tag = g_strdup("</font>");
+		span->buffer = imhtml->text_buffer;
+		span->tag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", color, NULL);
+		imhtml->edit.backcolor = span;
+		imhtml->format_spans = g_list_append(imhtml->format_spans, span);
+	} else {
+		span = imhtml->edit.backcolor;
+		span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		imhtml->edit.backcolor = NULL;
+	}
+	return imhtml->edit.backcolor != NULL;
+}
+
+gboolean gtk_imhtml_toggle_fontface(GtkIMHtml *imhtml, const char *face)
+{
+	GtkIMHtmlFormatSpan *span;
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter iter;
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
+	if (!imhtml->edit.fontface) {
+		span = g_malloc(sizeof(GtkIMHtmlFormatSpan));
+		span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		span->start_tag = g_strdup_printf("<font face=\"%s\">", face);
+		span->end = NULL;
+		span->end_tag = g_strdup("</font>");
+		span->buffer = imhtml->text_buffer;
+		span->tag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "family", face, NULL);
+		imhtml->edit.fontface = span;
+		imhtml->format_spans = g_list_append(imhtml->format_spans, span);
+	} else {
+		span = imhtml->edit.fontface;
+		span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+		imhtml->edit.fontface = NULL;
+	}
+	return imhtml->edit.fontface != NULL;
+}
+
+void gtk_imhtml_insert_link(GtkIMHtml *imhtml, const char *url, const char *text)
+{
+	GtkIMHtmlFormatSpan *span = g_malloc(sizeof(GtkIMHtmlFormatSpan));
+	GtkTextMark *mark = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter iter;
+	GtkTextTag *tag, *linktag;
+
+	tag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, NULL);
+	g_object_set_data(G_OBJECT(tag), "link_url", g_strdup(url));
+
+	linktag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), "LINK");
+
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
+	span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+	span->buffer = imhtml->text_buffer;
+	span->start_tag = g_strdup_printf("<a href=\"%s\">", url);
+	span->end_tag = g_strdup("</a>");
+	g_signal_connect(G_OBJECT(tag), "event", G_CALLBACK(tag_event), g_strdup(url));
+
+	gtk_text_buffer_insert_with_tags(imhtml->text_buffer, &iter, text, strlen(text), linktag, tag, NULL);
+	span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE);
+	imhtml->format_spans = g_list_append(imhtml->format_spans, span);
+}
+
+void gtk_imhtml_insert_smiley(GtkIMHtml *imhtml, const char *sml, char *smiley)
+{
+	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+	GtkTextIter iter;
+	GdkPixbuf *pixbuf = NULL;
+	GdkPixbufAnimation *annipixbuf = NULL;
+	GtkWidget *icon = NULL;
+	GtkTextChildAnchor *anchor;
+
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins);
+	anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, &iter);
+	g_object_set_data(G_OBJECT(anchor), "text_tag", g_strdup(smiley));
+
+	annipixbuf = gtk_smiley_tree_image(imhtml, sml, smiley);
+	if(annipixbuf) {
+		if(gdk_pixbuf_animation_is_static_image(annipixbuf)) {
+			pixbuf = gdk_pixbuf_animation_get_static_image(annipixbuf);
+			if(pixbuf)
+				icon = gtk_image_new_from_pixbuf(pixbuf);
+		} else {
+			icon = gtk_image_new_from_animation(annipixbuf);
+		}
+	}
+
+	if (icon) {
+		gtk_widget_show(icon);
+		gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), icon, anchor);
+	}
+}
+
+int span_compare_begin(const GtkIMHtmlFormatSpan *a, const GtkIMHtmlFormatSpan *b, GtkTextBuffer *buffer)
+{
+	GtkTextIter ia, ib;
+	gtk_text_buffer_get_iter_at_mark(buffer, &ia, a->start);
+	gtk_text_buffer_get_iter_at_mark(buffer, &ib, b->start);
+	return gtk_text_iter_compare(&ia, &ib);
+}
+
+int span_compare_end(GtkIMHtmlFormatSpan *a, GtkIMHtmlFormatSpan *b)
+{
+	GtkTextIter ia, ib;
+	gtk_text_buffer_get_iter_at_mark(a->buffer, &ia, a->start);
+	gtk_text_buffer_get_iter_at_mark(b->buffer, &ib, b->start);
+	/* The -1 here makes it so that if I have two spans that close at the same point, the
+	 * span added second will be closed first, as in <b><i>Hello</i></b>.  Without this,
+	 * it would be <b><i>Hello</b></i> */
+	return gtk_text_iter_compare(&ia, &ib) - 1;
+}
+
+/* Basic notion here: traverse through the text buffer one-by-one, non-character elements, such
+ * as smileys and IM images are represented by the Unicode "unknown" character.  Handle them.  Else
+ * check the list of formatted strings, sorted by the position of the starting tags and apply them as
+ * needed.  After applying the start tags, add the end tags to the "closers" list, which is sorted by
+ * location of ending tags.  These get applied in a similar fashion.  Finally, replace <, >, &, and "
+ * with their HTML equivilent. */
+char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end)
+{
+	gunichar c;
+	GtkIMHtmlFormatSpan *sspan = NULL, *espan = NULL;
+	GtkTextIter iter, siter, eiter;
+	GList *starters = imhtml->format_spans;
+	GList *closers = NULL;
+	GString *str = g_string_new("");
+	g_list_sort_with_data(starters, (GCompareDataFunc)span_compare_begin, imhtml->text_buffer);
+
+	gtk_text_iter_order(start, end);
+	iter = *start;
+
+
+	/* Initialize these to the end iter */
+	gtk_text_buffer_get_end_iter(imhtml->text_buffer, &siter);
+	eiter = siter;
+
+	if (starters) {
+		while (starters) {
+			GtkTextIter tagend;
+			sspan = (GtkIMHtmlFormatSpan*)starters->data;
+			gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &siter, sspan->start);
+			if (gtk_text_iter_compare(&siter, start) > 0)
+				break;
+			if (sspan->end)
+				gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &tagend, sspan->end);
+			if (sspan->end == NULL || gtk_text_iter_compare(&tagend, start) > 0) {
+				str = g_string_append(str, sspan->start_tag);
+				closers = g_list_insert_sorted(closers, sspan, (GCompareFunc)span_compare_end);
+				espan = (GtkIMHtmlFormatSpan*)closers->data;
+				gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &eiter, espan->end);
+			}
+			sspan = (GtkIMHtmlFormatSpan*)starters->data;
+			gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &siter, sspan->start);
+			starters = starters->next;
+		}
+		if (!starters) {
+			gtk_text_buffer_get_end_iter(imhtml->text_buffer, &siter);
+			sspan = NULL;
+		}
+	}
+
+	while ((c = gtk_text_iter_get_char(&iter)) != 0 && !gtk_text_iter_equal(&iter, end)) {
+		if (c == 0xFFFC) {
+			GtkTextChildAnchor* anchor = gtk_text_iter_get_child_anchor(&iter);
+			char *text = g_object_get_data(G_OBJECT(anchor), "text_tag");
+			str = g_string_append(str, text);
+		} else {
+			while (gtk_text_iter_equal(&eiter, &iter)) {
+				/* This is where we shall insert the ending tag of
+				 * this format span */
+				str = g_string_append(str, espan->end_tag);
+				closers = g_list_remove(closers, espan);
+				if (!closers) {
+					espan = NULL;
+					gtk_text_buffer_get_end_iter(imhtml->text_buffer, &eiter);
+				} else {
+					espan = (GtkIMHtmlFormatSpan*)closers->data;
+					gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &eiter, espan->end);
+				}
+			}
+			while (gtk_text_iter_equal(&siter, &iter)) {
+				/* This is where we shall insert the starting tag of
+				 * this format span */
+				str = g_string_append(str, sspan->start_tag);
+				if (sspan->end) {
+					closers = g_list_insert_sorted(closers, sspan, (GCompareFunc)span_compare_end);
+					espan = (GtkIMHtmlFormatSpan*)closers->data;
+					gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &eiter, espan->end);
+
+				}
+				starters = starters->next;
+				if (starters) {
+					sspan = (GtkIMHtmlFormatSpan*)starters->data;
+					gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &siter, sspan->start);
+				} else {
+					sspan = NULL;
+					gtk_text_buffer_get_end_iter(imhtml->text_buffer, &siter);
+				}
+
+			}
+
+			if (c == '<')
+				str = g_string_append(str, "&lt;");
+			else if (c == '>')
+				str = g_string_append(str, "&gt;");
+			else if (c == '&')
+				str = g_string_append(str, "&amp;");
+			else if (c == '"')
+				str = g_string_append(str, "&quot;");
+			else if (c == '\n')
+				str = g_string_append(str, "<br>");
+			else
+				str = g_string_append_unichar(str, c);
+		}
+		gtk_text_iter_forward_char(&iter);
+	}
+	while (closers) {
+		GtkIMHtmlFormatSpan *span = (GtkIMHtmlFormatSpan*)closers->data;
+		str = g_string_append(str, span->end_tag);
+		closers = g_list_remove(closers, span);
+
+	}
+	printf("Gotten: %s\n", str->str);
+	return g_string_free(str, FALSE);
+}
+
+void gtk_imhtml_close_tags(GtkIMHtml *imhtml)
+{
+
+	if (imhtml->edit.bold)
+		gtk_imhtml_toggle_bold(imhtml);
+
+	if (imhtml->edit.italic)
+		gtk_imhtml_toggle_italic(imhtml);
+
+	if (imhtml->edit.underline)
+		gtk_imhtml_toggle_underline(imhtml);
+
+	if (imhtml->edit.forecolor)
+		gtk_imhtml_toggle_forecolor(imhtml, NULL);
+
+	if (imhtml->edit.backcolor)
+		gtk_imhtml_toggle_backcolor(imhtml, NULL);
+
+	if (imhtml->edit.fontface)
+		gtk_imhtml_toggle_fontface(imhtml, NULL);
+
+	if (imhtml->edit.sizespan)
+		gtk_imhtml_font_set_size(imhtml, -1);
+
+}
+
+char *gtk_imhtml_get_markup(GtkIMHtml *imhtml)
+{
+	GtkTextIter start, end;
+
+	gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start);
+	gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end);
+	return gtk_imhtml_get_markup_range(imhtml, &start, &end);
+}
+
+char *gtk_imhtml_get_text(GtkIMHtml *imhtml)
+{
+	GtkTextIter start_iter, end_iter;
+	gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start_iter);
+	gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end_iter);
+	return gtk_text_buffer_get_text(imhtml->text_buffer, &start_iter, &end_iter, FALSE);
+
+}
+
--- a/src/gtkimhtml.h	Sat Jan 10 05:20:50 2004 +0000
+++ b/src/gtkimhtml.h	Sat Jan 10 06:06:02 2004 +0000
@@ -39,7 +39,6 @@
 #define GTK_IS_IMHTML(obj)         (GTK_CHECK_TYPE ((obj), GTK_TYPE_IMHTML))
 #define GTK_IS_IMHTML_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IMHTML))
 #define GTK_IMHTML_SCALABLE(obj)   ((GtkIMHtmlScalable *)obj)
-#define GTK_IMHTML_COPYABLE(obj)   ((GtkIMHtmlCopyable *)obj)
 
 typedef struct _GtkIMHtml			GtkIMHtml;
 typedef struct _GtkIMHtmlClass		GtkIMHtmlClass;
@@ -49,7 +48,16 @@
 typedef struct _GtkIMHtmlScalable	GtkIMHtmlScalable;
 typedef struct _GtkIMHtmlImage		GtkIMHtmlImage;
 typedef struct _GtkIMHtmlHr			GtkIMHtmlHr;
-typedef struct _GtkIMHtmlCopyable       GtkIMHtmlCopyable;
+
+
+typedef struct {
+	GtkTextMark *start;
+	GtkTextMark *end;
+	char *start_tag;
+	char *end_tag;
+	GtkTextBuffer *buffer;
+	GtkTextTag *tag;
+} GtkIMHtmlFormatSpan;
 
 struct _GtkIMHtml {
 	GtkTextView text_view;
@@ -58,6 +66,7 @@
 	gboolean comments, smileys;
 	GdkCursor *hand_cursor;
 	GdkCursor *arrow_cursor;
+	GdkCursor *text_cursor;
 	GHashTable *smiley_data;
 	GtkSmileyTree *default_smilies;
 
@@ -71,9 +80,21 @@
 	GList *scalables;
 	GdkRectangle old_rect;
 
-	GSList *copyables;
+	gchar *search_string;
 
-	gchar *search_string;
+	gboolean editable;
+	struct {
+		GtkIMHtmlFormatSpan *bold;
+		GtkIMHtmlFormatSpan *italic;
+		GtkIMHtmlFormatSpan *underline;
+		GtkIMHtmlFormatSpan *forecolor;
+		GtkIMHtmlFormatSpan *backcolor;
+		GtkIMHtmlFormatSpan *fontface;
+		GtkIMHtmlFormatSpan *sizespan;
+		int fontsize;
+	} edit;
+	char *clipboard_string;
+	GList *format_spans;
 };
 
 struct _GtkIMHtmlClass {
@@ -109,11 +130,6 @@
 	void (*free)(struct _GtkIMHtmlScalable *);
 };
 
-struct _GtkIMHtmlCopyable {
-	GtkTextMark *mark;
-	char *text;
-};
-
 struct _GtkIMHtmlImage {
 	GtkIMHtmlScalable scalable;
 	GtkImage *image;
@@ -184,6 +200,24 @@
 gboolean gtk_imhtml_search_find(GtkIMHtml *imhtml, const gchar *text);
 void gtk_imhtml_search_clear(GtkIMHtml *imhtml);
 
+/* Editable stuff */
+void gtk_imhtml_set_editable(GtkIMHtml *imhtml, gboolean editable);
+gboolean gtk_imhtml_get_editable(GtkIMHtml *imhtml);
+gboolean gtk_imhtml_toggle_bold(GtkIMHtml *imhtml);
+gboolean gtk_imhtml_toggle_italic(GtkIMHtml *imhtml);
+gboolean gtk_imhtml_toggle_underline(GtkIMHtml *imhtml);
+gboolean gtk_imhtml_toggle_forecolor(GtkIMHtml *imhtml, const char *color);
+gboolean gtk_imhtml_toggle_backcolor(GtkIMHtml *imhtml, const char *color);
+gboolean gtk_imhtml_toggle_fontface(GtkIMHtml *imhtml, const char *face);
+void gtk_imhtml_insert_link(GtkIMHtml *imhtml, const char *url, const char *text);
+void gtk_imhtml_insert_smiley(GtkIMHtml *imhtml, const char *sml, char *smiley);
+void gtk_imhtml_font_set_size(GtkIMHtml *imhtml, gint size);
+void gtk_imhtml_font_shrink(GtkIMHtml *imhtml);
+void gtk_imhtml_font_grow(GtkIMHtml *imhtml);
+char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end);
+char *gtk_imhtml_get_markup(GtkIMHtml *imhtml);
+char *gtk_imhtml_get_text(GtkIMHtml *imhtml);
+
 #ifdef __cplusplus
 }
 #endif
--- a/src/gtkutils.c	Sat Jan 10 05:20:50 2004 +0000
+++ b/src/gtkutils.c	Sat Jan 10 06:06:02 2004 +0000
@@ -242,8 +242,6 @@
 gaim_gtk_set_font_face(GaimGtkConversation *gtkconv,
 					   const char *font)
 {
-	char *pre_fontface;
-
 	if (gtkconv == NULL || font == NULL)
 		return;
 
@@ -253,12 +251,10 @@
 
 	gtkconv->has_font = TRUE;
 
-	pre_fontface = g_strconcat("<FONT FACE=\"", gtkconv->fontface, "\">", NULL);
-	gaim_gtk_surround(gtkconv, pre_fontface, "</FONT>");
+	gtk_imhtml_toggle_fontface(GTK_IMHTML(gtkconv->entry), gtkconv->fontface);
 
 	gtk_widget_grab_focus(gtkconv->entry);
 
-	g_free(pre_fontface);
 }
 
 static int