diff pidgin/gtkconv.c @ 23394:eac0561dfd55

propagate from branch 'im.pidgin.pidgin.2.4.3' (head 260d5ee6ca276e761c5b0bd1b612edc5743eaf67) to branch 'im.pidgin.pidgin' (head dba36543cdde6db127857b0edfdc3ad1969bbd39)
author Stu Tomlinson <stu@nosnilmot.com>
date Thu, 26 Jun 2008 19:08:55 +0000
parents 398f84dd4041
children a5bfc59b0c6e 3da0957e7821 a5bb767b8644
line wrap: on
line diff
--- a/pidgin/gtkconv.c	Thu Jun 26 19:08:38 2008 +0000
+++ b/pidgin/gtkconv.c	Thu Jun 26 19:08:55 2008 +0000
@@ -96,10 +96,10 @@
 
 #define	PIDGIN_CONV_ALL	((1 << 7) - 1)
 
+/* XXX: These color defines shouldn't really be here. But the nick-color
+ * generation algorithm uses them, so keeping these around until we fix that. */
 #define DEFAULT_SEND_COLOR "#204a87"
-#define DEFAULT_RECV_COLOR "#cc0000"
 #define DEFAULT_HIGHLIGHT_COLOR "#AF7F00"
-#define DEFAULT_ACTION_COLOR "#062585"
 
 #define BUDDYICON_SIZE_MIN    32
 #define BUDDYICON_SIZE_MAX    96
@@ -159,10 +159,9 @@
 static void update_typing_message(PidginConversation *gtkconv, const char *message);
 static const char *item_factory_translate_func (const char *path, gpointer func_data);
 gboolean pidgin_conv_has_focus(PurpleConversation *conv);
-static void pidgin_conv_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data);
-static void pidgin_conv_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data);
 static GdkColor* generate_nick_colors(guint *numcolors, GdkColor background);
 static gboolean color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast);
+static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, gboolean create);
 static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields);
 static void focus_out_from_menubar(GtkWidget *wid, PidginWindow *win);
 static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv);
@@ -1482,7 +1481,7 @@
 	PurpleAccount *account;
 	PurpleConnection *gc;
 	PurplePluginProtocolInfo *prpl_info = NULL;
-	char *real_who;
+	gchar *real_who = NULL;
 
 	account = purple_conversation_get_account(conv);
 	g_return_if_fail(account != NULL);
@@ -1495,13 +1494,11 @@
 	if (prpl_info && prpl_info->get_cb_real_name)
 		real_who = prpl_info->get_cb_real_name(gc,
 				purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
-	else
-		real_who = g_strdup(who);
-
-	if(!real_who)
+
+	if(!who && !real_who)
 		return;
 
-	pidgin_dialogs_im_with_user(account, real_who);
+	pidgin_dialogs_im_with_user(account, real_who ? real_who : who);
 
 	g_free(real_who);
 }
@@ -1540,11 +1537,22 @@
 static void
 menu_chat_send_file_cb(GtkWidget *w, PidginConversation *gtkconv)
 {
+	PurplePluginProtocolInfo *prpl_info;
 	PurpleConversation *conv = gtkconv->active_conv;
 	const char *who = g_object_get_data(G_OBJECT(w), "user_data");
 	PurpleConnection *gc  = purple_conversation_get_gc(conv);
-
-	serv_send_file(gc, who, NULL);
+	gchar *real_who = NULL;
+
+	g_return_if_fail(gc != NULL);
+
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+	if (prpl_info && prpl_info->get_cb_real_name)
+		real_who = prpl_info->get_cb_real_name(gc,
+				purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
+
+	serv_send_file(gc, real_who ? real_who : who, NULL);
+	g_free(real_who);
 }
 
 static void
@@ -1660,23 +1668,34 @@
 
 		if (gc == NULL)
 			gtk_widget_set_sensitive(button, FALSE);
-
-		g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+		else
+			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 
 
 		if (prpl_info && prpl_info->send_file)
 		{
+			gboolean can_receive_file = TRUE;
+
 			button = pidgin_new_item_from_stock(menu, _("Send File"),
 				PIDGIN_STOCK_TOOLBAR_SEND_FILE, G_CALLBACK(menu_chat_send_file_cb),
 				PIDGIN_CONVERSATION(conv), 0, 0, NULL);
 
-			if (gc == NULL || prpl_info == NULL ||
-			    !(!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, who)))
-			{
+			if (gc == NULL || prpl_info == NULL)
+				can_receive_file = FALSE;
+			else {
+				gchar *real_who = NULL;
+				if (prpl_info->get_cb_real_name)
+					real_who = prpl_info->get_cb_real_name(gc,
+						purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
+				if (!(!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, real_who ? real_who : who)))
+					can_receive_file = FALSE;
+				g_free(real_who);
+			}
+
+			if (!can_receive_file)
 				gtk_widget_set_sensitive(button, FALSE);
-			}
-
-			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+			else
+				g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 		}
 
 
@@ -1689,8 +1708,8 @@
 
 		if (gc == NULL)
 			gtk_widget_set_sensitive(button, FALSE);
-
-		g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+		else
+			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 	}
 
 	if (prpl_info && (prpl_info->get_info || prpl_info->get_cb_info)) {
@@ -1699,8 +1718,8 @@
 
 		if (gc == NULL)
 			gtk_widget_set_sensitive(button, FALSE);
-
-		g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+		else
+			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 	}
 
 	if (prpl_info && prpl_info->get_cb_away) {
@@ -1709,8 +1728,8 @@
 
 		if (gc == NULL)
 			gtk_widget_set_sensitive(button, FALSE);
-
-		g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+		else
+			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 	}
 
 	if (!is_me && prpl_info && !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
@@ -1723,8 +1742,8 @@
 
 		if (gc == NULL)
 			gtk_widget_set_sensitive(button, FALSE);
-
-		g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+		else
+			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 	}
 
 	button = pidgin_new_item_from_stock(menu, _("Last said"), GTK_STOCK_INDEX,
@@ -2513,10 +2532,6 @@
 		PurpleBuddy *b = purple_find_buddy(conv->account, conv->name);
 		if (b)
 			emblem = pidgin_blist_get_emblem((PurpleBlistNode*)b);
-	} else {
-		PurpleChat *c = purple_blist_find_chat(conv->account, conv->name);
-		if (c)
-			emblem = pidgin_blist_get_emblem((PurpleBlistNode*)c);
 	}
 
 	g_return_if_fail(status != NULL);
@@ -2724,10 +2739,22 @@
 custom_icon_sel_cb(const char *filename, gpointer data)
 {
 	if (filename) {
+		const gchar *name;
+		PurpleBuddy *buddy;
+		PurpleContact *contact;
 		PidginConversation *gtkconv = data;
 		PurpleConversation *conv = gtkconv->active_conv;
 		PurpleAccount *account = purple_conversation_get_account(conv);
-		pidgin_set_custom_buddy_icon(account, purple_conversation_get_name(conv), filename);
+
+		name = purple_conversation_get_name(conv);
+		buddy = purple_find_buddy(account, name);
+		if (!buddy) {
+			purple_debug_info("custom-icon", "You can only set custom icons for people on your buddylist.\n");
+			return;
+		}
+		contact = purple_buddy_get_contact(buddy);
+
+		purple_buddy_icons_node_set_custom_icon_from_file((PurpleBlistNode*)contact, filename);
 	}
 }
 
@@ -2769,12 +2796,21 @@
 static void
 remove_custom_icon_cb(GtkWidget *widget, PidginConversation *gtkconv)
 {
-	PurpleConversation *conv;
+	const gchar *name;
+	PurpleBuddy *buddy;
 	PurpleAccount *account;
-
-	conv = gtkconv->active_conv;
+	PurpleContact *contact;
+	PurpleConversation *conv = gtkconv->active_conv;
+
 	account = purple_conversation_get_account(conv);
-	pidgin_set_custom_buddy_icon(account, purple_conversation_get_name(conv), NULL);
+	name = purple_conversation_get_name(conv);
+	buddy = purple_find_buddy(account, name);
+	if (!buddy) {
+		return;
+	}
+	contact = purple_buddy_get_contact(buddy);
+
+	purple_buddy_icons_node_set_custom_icon_from_file((PurpleBlistNode*)contact, NULL);
 }
 
 static void
@@ -2875,7 +2911,7 @@
 	if (buddy)
 	{
 		PurpleContact *contact = purple_buddy_get_contact(buddy);
-		if (contact && purple_buddy_icons_has_custom_icon(contact))
+		if (contact && purple_buddy_icons_node_has_custom_icon((PurpleBlistNode*)contact))
 		{
 			pidgin_new_item_from_stock(menu, _("Remove Custom Icon"), NULL,
 			                           G_CALLBACK(remove_custom_icon_cb), gtkconv,
@@ -3137,10 +3173,19 @@
 
 		if ((chat == NULL) && (gtkconv->imhtml != NULL)) {
 			GHashTable *components;
-			components = g_hash_table_new_full(g_str_hash, g_str_equal,
-					g_free, g_free);
-			g_hash_table_replace(components, g_strdup("channel"),
-					g_strdup(conv->name));
+			PurpleAccount *account = purple_conversation_get_account(conv);
+			PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account));
+			PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+			if (purple_account_get_connection(account) != NULL &&
+					PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_info_defaults)) {
+				components = prpl_info->chat_info_defaults(purple_account_get_connection(account),
+						purple_conversation_get_name(conv));
+			} else {
+				components = g_hash_table_new_full(g_str_hash, g_str_equal,
+						g_free, g_free);
+				g_hash_table_replace(components, g_strdup("channel"),
+						g_strdup(purple_conversation_get_name(conv)));
+			}
 			chat = purple_chat_new(conv->account, NULL, components);
 			purple_blist_node_set_flags((PurpleBlistNode *)chat,
 					PURPLE_BLIST_NODE_FLAG_NO_SAVE);
@@ -3875,6 +3920,7 @@
 	gboolean is_buddy;
 	gchar *tmp, *alias_key, *name, *alias;
 	int flags;
+	GdkColor *color = NULL;
 
 	alias = cb->alias;
 	name  = cb->name;
@@ -3901,71 +3947,50 @@
 	alias_key = g_utf8_collate_key(tmp, -1);
 	g_free(tmp);
 
-	if (is_me)
-	{
-		GdkColor send_color;
-		gdk_color_parse(DEFAULT_SEND_COLOR, &send_color);
+	if (is_me) {
+		GtkTextTag *tag = gtk_text_tag_table_lookup(
+				gtk_text_buffer_get_tag_table(GTK_IMHTML(gtkconv->imhtml)->text_buffer),
+				"send-name");
+		g_object_get(tag, "foreground-gdk", &color, NULL);
+	} else {
+		color = (GdkColor*)get_nick_color(gtkconv, name);
+	}
 
 #if GTK_CHECK_VERSION(2,6,0)
-		gtk_list_store_insert_with_values(ls, &iter,
+	gtk_list_store_insert_with_values(ls, &iter,
 /*
- * The GTK docs are mute about the effects of the "row" value for performance.
- * X-Chat hardcodes their value to 0 (prepend) and -1 (append), so we will too.
- * It *might* be faster to search the gtk_list_store and set row accurately,
- * but no one in #gtk+ seems to know anything about it either.
- * Inserting in the "wrong" location has no visible ill effects. - F.P.
- */
-		                                  -1, /* "row" */
-		                                  CHAT_USERS_ICON_COLUMN,  pixbuf,
-		                                  CHAT_USERS_ALIAS_COLUMN, alias,
-		                                  CHAT_USERS_ALIAS_KEY_COLUMN, alias_key,
-		                                  CHAT_USERS_NAME_COLUMN,  name,
-		                                  CHAT_USERS_FLAGS_COLUMN, flags,
-		                                  CHAT_USERS_COLOR_COLUMN, &send_color,
-		                                  CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
-		                                  -1);
-	}
-	else
-	{
-		gtk_list_store_insert_with_values(ls, &iter,
-		                                  -1, /* "row" */
-		                                  CHAT_USERS_ICON_COLUMN,  pixbuf,
-		                                  CHAT_USERS_ALIAS_COLUMN, alias,
-		                                  CHAT_USERS_ALIAS_KEY_COLUMN, alias_key,
-		                                  CHAT_USERS_NAME_COLUMN,  name,
-		                                  CHAT_USERS_FLAGS_COLUMN, flags,
-		                                  CHAT_USERS_COLOR_COLUMN, get_nick_color(gtkconv, name),
-		                                  CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
-		                                  -1);
+* The GTK docs are mute about the effects of the "row" value for performance.
+* X-Chat hardcodes their value to 0 (prepend) and -1 (append), so we will too.
+* It *might* be faster to search the gtk_list_store and set row accurately,
+* but no one in #gtk+ seems to know anything about it either.
+* Inserting in the "wrong" location has no visible ill effects. - F.P.
+*/
+			-1, /* "row" */
+			CHAT_USERS_ICON_COLUMN,  pixbuf,
+			CHAT_USERS_ALIAS_COLUMN, alias,
+			CHAT_USERS_ALIAS_KEY_COLUMN, alias_key,
+			CHAT_USERS_NAME_COLUMN,  name,
+			CHAT_USERS_FLAGS_COLUMN, flags,
+			CHAT_USERS_COLOR_COLUMN, color,
+			CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
+			-1);
 #else
-		gtk_list_store_append(ls, &iter);
-		gtk_list_store_set(ls, &iter,
-		                   CHAT_USERS_ICON_COLUMN,  pixbuf,
-		                   CHAT_USERS_ALIAS_COLUMN, alias,
-		                   CHAT_USERS_ALIAS_KEY_COLUMN, alias_key,
-		                   CHAT_USERS_NAME_COLUMN,  name,
-		                   CHAT_USERS_FLAGS_COLUMN, flags,
-		                   CHAT_USERS_COLOR_COLUMN, &send_color,
-		                   CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
-		                   -1);
-	}
-	else
-	{
-		gtk_list_store_append(ls, &iter);
-		gtk_list_store_set(ls, &iter,
-		                   CHAT_USERS_ICON_COLUMN,  pixbuf,
-		                   CHAT_USERS_ALIAS_COLUMN, alias,
-		                   CHAT_USERS_ALIAS_KEY_COLUMN, alias_key,
-		                   CHAT_USERS_NAME_COLUMN,  name,
-		                   CHAT_USERS_FLAGS_COLUMN, flags,
-		                   CHAT_USERS_COLOR_COLUMN, get_nick_color(gtkconv, name),
-		                   CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
-		                   -1);
+	gtk_list_store_append(ls, &iter);
+	gtk_list_store_set(ls, &iter,
+			CHAT_USERS_ICON_COLUMN,  pixbuf,
+			CHAT_USERS_ALIAS_COLUMN, alias,
+			CHAT_USERS_ALIAS_KEY_COLUMN, alias_key,
+			CHAT_USERS_NAME_COLUMN,  name,
+			CHAT_USERS_FLAGS_COLUMN, flags,
+			CHAT_USERS_COLOR_COLUMN, color,
+			CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
+			-1);
 #endif
-	}
 
 	if (pixbuf)
 		g_object_unref(pixbuf);
+	if (is_me && color)
+		gdk_color_free(color);
 	g_free(alias_key);
 }
 
@@ -4359,6 +4384,7 @@
 	GtkTreeModel *model;
 	char *normalized_name;
 	GtkTreeIter iter;
+	GtkTextTag *texttag;
 	int f;
 
 	g_return_if_fail(buddy != NULL);
@@ -4396,6 +4422,11 @@
 	g_free(normalized_name);
 
 	blist_node_aliased_cb((PurpleBlistNode *)buddy, NULL, conv);
+
+	texttag = get_buddy_tag(conv, purple_buddy_get_name(buddy), FALSE); /* XXX: do we want the normalized name? */
+	if (texttag) {
+		g_object_set(texttag, "weight", is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, NULL);
+	}
 }
 
 static void
@@ -5180,6 +5211,9 @@
 		nbr_nick_colors = NUM_NICK_COLORS;
 		nick_colors = generate_nick_colors(&nbr_nick_colors, gtk_widget_get_style(gtkconv->imhtml)->base[GTK_STATE_NORMAL]);
 	}
+
+	if (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY)
+		pidgin_themes_smiley_themeize_custom(gtkconv->entry);
 }
 
 static void
@@ -5356,7 +5390,8 @@
 	return FALSE;
 }
 
-static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who) {
+static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, gboolean create)
+{
 	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 	GtkTextTag *buddytag;
 	gchar *str;
@@ -5367,9 +5402,12 @@
 			gtk_text_buffer_get_tag_table(
 				GTK_IMHTML(gtkconv->imhtml)->text_buffer), str);
 
-	if (buddytag == NULL) {
+	if (buddytag == NULL && create) {
 		buddytag = gtk_text_buffer_create_tag(
-				GTK_IMHTML(gtkconv->imhtml)->text_buffer, str, NULL);
+				GTK_IMHTML(gtkconv->imhtml)->text_buffer, str,
+				"foreground-gdk", get_nick_color(gtkconv, who),
+				"weight", purple_find_buddy(purple_conversation_get_account(conv), who) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
+				NULL);
 
 		g_signal_connect(G_OBJECT(buddytag), "event",
 				G_CALLBACK(buddytag_event), conv);
@@ -5475,7 +5513,6 @@
 	char buf2[BUF_LONG];
 	gboolean show_date;
 	char *mdate;
-	char color[10];
 	char *str;
 	char *with_font_tag;
 	char *sml_attrib = NULL;
@@ -5486,8 +5523,6 @@
 	char *bracket;
 	int tag_count = 0;
 	gboolean is_rtl_message = FALSE;
-	GtkSmileyTree *tree = NULL;
-	GHashTable *smiley_data = NULL;
 
 	g_return_if_fail(conv != NULL);
 	gtkconv = PIDGIN_CONVERSATION(conv);
@@ -5644,16 +5679,10 @@
 		gtk_font_options |= GTK_IMHTML_USE_POINTSIZE;
 	}
 
-	if (!(flags & PURPLE_MESSAGE_RECV))
+	if (!(flags & PURPLE_MESSAGE_RECV) && (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY))
 	{
-		/* Temporarily revert to the original smiley-data to avoid showing up
-		 * custom smileys of the buddy when sending message
-		 */
-		tree = GTK_IMHTML(gtkconv->imhtml)->default_smilies;
-		GTK_IMHTML(gtkconv->imhtml)->default_smilies =
-								GTK_IMHTML(gtkconv->entry)->default_smilies;
-		smiley_data = GTK_IMHTML(gtkconv->imhtml)->smiley_data;
-		GTK_IMHTML(gtkconv->imhtml)->smiley_data = GTK_IMHTML(gtkconv->entry)->smiley_data;
+		/* We want to see our own smileys. Need to revert it after send*/
+		pidgin_themes_smiley_themeize_custom(gtkconv->imhtml);
 	}
 
 	/* TODO: These colors should not be hardcoded so log.c can use them */
@@ -5684,169 +5713,109 @@
 		char *alias_escaped = (alias ? g_markup_escape_text(alias, strlen(alias)) : g_strdup(""));
 		/* The initial offset is to deal with
 		 * escaped entities making the string longer */
-		int tag_start_offset = alias ? (strlen(alias_escaped) - strlen(alias)) : 0;
+		int tag_start_offset = 0;
 		int tag_end_offset = 0;
+		const char *tagname = NULL;
+
+		GtkTextIter start, end;
+		GtkTextMark *mark;
+		GtkTextTag *tag;
+		GtkTextBuffer *buffer = GTK_IMHTML(gtkconv->imhtml)->text_buffer;
 
 		/* Enforce direction on alias */
 		if (is_rtl_message)
 			str_embed_direction_chars(&alias_escaped);
 
+		str = g_malloc(1024);
 		if (flags & PURPLE_MESSAGE_WHISPER) {
-			str = g_malloc(1024);
-
 			/* If we're whispering, it's not an autoresponse. */
 			if (purple_message_meify(new_message, -1 )) {
 				g_snprintf(str, 1024, "***%s", alias_escaped);
-				strcpy(color, "#6C2585");
 				tag_start_offset += 3;
+				tagname = "whisper-action-name";
 			}
 			else {
 				g_snprintf(str, 1024, "*%s*:", alias_escaped);
 				tag_start_offset += 1;
 				tag_end_offset = 2;
-				strcpy(color, "#00FF00");
+				tagname = "whisper-name";
 			}
-		}
-		else {
+		} else {
 			if (purple_message_meify(new_message, -1)) {
-				GdkColor *col;
-				str = g_malloc(1024);
-
 				if (flags & PURPLE_MESSAGE_AUTO_RESP) {
 					g_snprintf(str, 1024, "%s ***%s", AUTO_RESPONSE, alias_escaped);
-					tag_start_offset += 4
-						+ strlen(AUTO_RESPONSE);
+					tag_start_offset += strlen(AUTO_RESPONSE) - 6 + 4;
 				} else {
 					g_snprintf(str, 1024, "***%s", alias_escaped);
 					tag_start_offset += 3;
 				}
 
 				if (flags & PURPLE_MESSAGE_NICK)
-					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "highlight-name-color", &col, NULL);
+					tagname = "highlight-name";
 				else
-					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "action-name-color", &col, NULL);
-
-				if(col) {
-					g_snprintf(color, sizeof(color), "#%02X%02X%02X",
-						col->red >> 8, col->green >> 8, col->blue >> 8);
-					gdk_color_free(col);
-				} else {
-					if (flags & PURPLE_MESSAGE_NICK)
-						strcpy(color, DEFAULT_HIGHLIGHT_COLOR);
-					else
-						strcpy(color, DEFAULT_ACTION_COLOR);
-				}
-			}
-			else {
-				str = g_malloc(1024);
+					tagname = "action-name";
+			} else {
 				if (flags & PURPLE_MESSAGE_AUTO_RESP) {
 					g_snprintf(str, 1024, "%s %s", alias_escaped, AUTO_RESPONSE);
-					tag_start_offset += 1
-						+ strlen(AUTO_RESPONSE);
+					tag_start_offset += strlen(AUTO_RESPONSE) - 6 + 1;
 				} else {
 					g_snprintf(str, 1024, "%s:", alias_escaped);
 					tag_end_offset = 1;
 				}
+
 				if (flags & PURPLE_MESSAGE_NICK) {
-					GdkColor *col;
-					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "highlight-name-color", &col, NULL);
-					if(col) {
-						g_snprintf(color, sizeof(color), "#%02X%02X%02X",
-							col->red >> 8, col->green >> 8, col->blue >> 8);
-						gdk_color_free(col);
-					} else {
-						strcpy(color, DEFAULT_HIGHLIGHT_COLOR);
+					tagname = "highlight-name";
+				} else if (flags & PURPLE_MESSAGE_RECV) {
+					/* The tagname for chats is handled by get_buddy_tag */
+					if (type == PURPLE_CONV_TYPE_IM) {
+						tagname = "receive-name";
 					}
-				}
-				else if (flags & PURPLE_MESSAGE_RECV) {
-					if (type == PURPLE_CONV_TYPE_CHAT) {
-						const GdkColor *col = get_nick_color(gtkconv, name);
-
-						g_snprintf(color, sizeof(color), "#%02X%02X%02X",
-							   col->red >> 8, col->green >> 8, col->blue >> 8);
-					} else {
-						GdkColor *col;
-						gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "receive-name-color", &col, NULL);
-						if(col) {
-							g_snprintf(color, sizeof(color), "#%02X%02X%02X",
-								col->red >> 8, col->green >> 8, col->blue >> 8);
-							gdk_color_free(col);
-						} else {
-							strcpy(color, DEFAULT_RECV_COLOR);
-						}
-					}
-				}
-				else if (flags & PURPLE_MESSAGE_SEND) {
-					GdkColor *col;
-					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "send-name-color", &col, NULL);
-					if(col) {
-						g_snprintf(color, sizeof(color), "#%02X%02X%02X",
-							col->red >> 8, col->green >> 8, col->blue >> 8);
-						gdk_color_free(col);
-					} else {
-						strcpy(color, DEFAULT_SEND_COLOR);
-					}
-				}
-				else {
+				} else if (flags & PURPLE_MESSAGE_SEND) {
+					tagname = "send-name";
+				} else {
 					purple_debug_error("gtkconv", "message missing flags\n");
-					strcpy(color, "#000000");
 				}
 			}
 		}
 
 		g_free(alias_escaped);
 
-		/* Are we in a chat where we can tell which users are buddies? */
-		if  (prpl_info && !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME) &&
-		     purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
-
-			/* Bold buddies to make them stand out from non-buddies. */
-			if (flags & PURPLE_MESSAGE_SEND ||
-			    flags & PURPLE_MESSAGE_NICK ||
-			    purple_find_buddy(account, name) != NULL) {
-				g_snprintf(buf2, BUF_LONG,
-					   "<FONT COLOR=\"%s\" %s><FONT SIZE=\"2\"><!--%s --></FONT>"
-					   "<B>%s</B></FONT> ",
-					   color, sml_attrib ? sml_attrib : "", mdate, str);
-			} else {
-				g_snprintf(buf2, BUF_LONG,
-					   "<FONT COLOR=\"%s\" %s><FONT SIZE=\"2\"><!--%s --></FONT>"
-					   "%s</FONT> ",
-					   color, sml_attrib ? sml_attrib : "", mdate, str);
-
-			}
-		} else {
-			/* Bold everyone's name to make the name stand out from the message. */
-			g_snprintf(buf2, BUF_LONG,
-				   "<FONT COLOR=\"%s\" %s><FONT SIZE=\"2\"><!--%s --></FONT>"
-				   "<B>%s</B></FONT> ",
-				   color, sml_attrib ? sml_attrib : "", mdate, str);
-		}
-
+		if (tagname)
+			tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tagname);
+		else
+			tag = get_buddy_tag(conv, name, TRUE);
+
+		if (GTK_IMHTML(gtkconv->imhtml)->show_comments) {
+			/* The color for the timestamp has to be set in the font-tags, unfortunately.
+			 * Applying the nick-tag to timestamps would work, but that can make it
+			 * bold. I thought applying the "comment" tag again, which has "weight" set
+			 * to PANGO_WEIGHT_NORMAL, would remove the boldness. But it doesn't. So
+			 * this will have to do. I don't terribly like it.  -- sadrul */
+			GdkColor *color = NULL;
+			gboolean set = FALSE;
+			char colcode[] = "COLOR=\"#XXXXXX\"";
+			g_object_get(G_OBJECT(tag), "foreground-set", &set, "foreground-gdk", &color, NULL);
+			if (set && color)
+				g_snprintf(colcode, sizeof(colcode), "COLOR=\"#%02x%02x%02x\"",
+						color->red >> 8, color->green >> 8, color->blue >> 8);
+			else
+				colcode[0] = '\0';
+			g_snprintf(buf2, BUF_LONG, "<FONT %s SIZE=\"2\"><!--%s --></FONT>", colcode, mdate);
+			gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL);
+			if (color)
+				gdk_color_free(color);
+		}
+
+		gtk_text_buffer_get_end_iter(buffer, &end);
+		mark = gtk_text_buffer_create_mark(buffer, NULL, &end, TRUE);
+
+		g_snprintf(buf2, BUF_LONG, "<FONT %s>%s</FONT> ", sml_attrib ? sml_attrib : "", str);
 		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL);
 
-		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
-		    !(flags & PURPLE_MESSAGE_SEND)) {
-
-			GtkTextIter start, end;
-			GtkTextTag *buddytag = get_buddy_tag(conv, name);
-
-			gtk_text_buffer_get_end_iter(
-					GTK_IMHTML(gtkconv->imhtml)->text_buffer,
-					&end);
-			gtk_text_iter_backward_chars(&end,
-					tag_end_offset + 1);
-
-			gtk_text_buffer_get_end_iter(
-					GTK_IMHTML(gtkconv->imhtml)->text_buffer,
-					&start);
-			gtk_text_iter_backward_chars(&start,
-					strlen(str) + 1 - tag_start_offset);
-
-			gtk_text_buffer_apply_tag(
-					GTK_IMHTML(gtkconv->imhtml)->text_buffer,
-					buddytag, &start, &end);
-		}
+		gtk_text_buffer_get_end_iter(buffer, &end);
+		gtk_text_buffer_get_iter_at_mark(buffer, &start, mark);
+		gtk_text_buffer_apply_tag(buffer, tag, &start, &end);
+		gtk_text_buffer_delete_mark(buffer, mark);
 
 		g_free(str);
 
@@ -5864,8 +5833,7 @@
 
 			length += pre_len + post_len;
 			g_free(pre);
-		}
-		else
+		} else
 			with_font_tag = g_memdup(new_message, length);
 
 		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml),
@@ -5896,11 +5864,10 @@
 		gtkconv_set_unseen(gtkconv, unseen);
 	}
 
-	if (!(flags & PURPLE_MESSAGE_RECV))
+	if (!(flags & PURPLE_MESSAGE_RECV) && (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY))
 	{
 		/* Restore the smiley-data */
-		GTK_IMHTML(gtkconv->imhtml)->default_smilies = tree;
-		GTK_IMHTML(gtkconv->imhtml)->smiley_data = smiley_data;
+		pidgin_themes_smiley_themeize(gtkconv->imhtml);
 	}
 
 	purple_signal_emit(pidgin_conversations_get_handle(),
@@ -6128,119 +6095,24 @@
 	return FALSE;
 }
 
-static void pidgin_conv_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data)
-{
-	GtkIMHtmlSmiley *smiley;
-
-	smiley = (GtkIMHtmlSmiley *)user_data;
-	smiley->icon = gdk_pixbuf_loader_get_animation(loader);
-
-	if (smiley->icon)
-		g_object_ref(G_OBJECT(smiley->icon));
-#ifdef DEBUG_CUSTOM_SMILEY
-	purple_debug_info("custom-smiley", "pidgin_conv_custom_smiley_allocated(): got GdkPixbufAnimation %p for smiley '%s'\n", smiley->icon, smiley->smile);
-#endif
-}
-
-static void pidgin_conv_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data)
-{
-	GtkIMHtmlSmiley *smiley;
-	GtkWidget *icon = NULL;
-	GtkTextChildAnchor *anchor = NULL;
-	GSList *current = NULL;
-
-	smiley = (GtkIMHtmlSmiley *)user_data;
-	if (!smiley->imhtml) {
-#ifdef DEBUG_CUSTOM_SMILEY
-		purple_debug_error("custom-smiley", "pidgin_conv_custom_smiley_closed(): orphan smiley found: %p\n", smiley);
-#endif
-		g_object_unref(G_OBJECT(loader));
-		smiley->loader = NULL;
-		return;
-	}
-
-	for (current = smiley->anchors; current; current = g_slist_next(current)) {
-
-		icon = gtk_image_new_from_animation(smiley->icon);
-
-#ifdef DEBUG_CUSTOM_SMILEY
-		purple_debug_info("custom-smiley", "pidgin_conv_custom_smiley_closed(): got GtkImage %p from GtkPixbufAnimation %p for smiley '%s'\n",
-				icon, smiley->icon, smiley->smile);
-#endif
-		if (icon) {
-			GList *wids;
-			gtk_widget_show(icon);
-
-			anchor = GTK_TEXT_CHILD_ANCHOR(current->data);
-			wids = gtk_text_child_anchor_get_widgets(anchor);
-
-			g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", purple_unescape_html(smiley->smile), g_free);
-			g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", g_strdup(smiley->smile), g_free);
-
-			if (smiley->imhtml) {
-				if (wids) {
-					GList *children = gtk_container_get_children(GTK_CONTAINER(wids->data));
-					g_list_foreach(children, (GFunc)gtk_widget_destroy, NULL);
-					g_list_free(children);
-					gtk_container_add(GTK_CONTAINER(wids->data), icon);
-				} else
-					gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(smiley->imhtml), icon, anchor);
-			}
-			g_list_free(wids);
-		}
-
-	}
-
-	g_slist_free(smiley->anchors);
-	smiley->anchors = NULL;
-
-	g_object_unref(G_OBJECT(loader));
-	smiley->loader = NULL;
-}
-
 static gboolean
 add_custom_smiley_for_imhtml(GtkIMHtml *imhtml, const char *sml, const char *smile)
 {
 	GtkIMHtmlSmiley *smiley;
-	GdkPixbufLoader *loader;
 
 	smiley = gtk_imhtml_smiley_get(imhtml, sml, smile);
 
 	if (smiley) {
-
 		if (!(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) {
 			return FALSE;
 		}
-
-		/* Close the old GdkPixbufAnimation, then create a new one for
-		 * the smiley we are about to receive */
-		g_object_unref(G_OBJECT(smiley->icon));
-
-		/* XXX: Is it necessary to _unref the loader first? */
-		smiley->loader = gdk_pixbuf_loader_new();
-		smiley->icon = NULL;
-
-		g_signal_connect(smiley->loader, "area_prepared", G_CALLBACK(pidgin_conv_custom_smiley_allocated), smiley);
-		g_signal_connect(smiley->loader, "closed", G_CALLBACK(pidgin_conv_custom_smiley_closed), smiley);
-
+		gtk_imhtml_smiley_reload(smiley);
 		return TRUE;
 	}
 
-	loader = gdk_pixbuf_loader_new();
-
-	/* this is wrong, this file ought not call g_new on GtkIMHtmlSmiley */
-	/* Let gtk_imhtml have a gtk_imhtml_smiley_new function, and let
-	   GtkIMHtmlSmiley by opaque */
-	smiley = g_new0(GtkIMHtmlSmiley, 1);
-	smiley->file   = NULL;
-	smiley->smile  = g_strdup(smile);
-	smiley->loader = loader;
-	smiley->flags  = smiley->flags | GTK_IMHTML_SMILEY_CUSTOM;
-
-	g_signal_connect(smiley->loader, "area_prepared", G_CALLBACK(pidgin_conv_custom_smiley_allocated), smiley);
-	g_signal_connect(smiley->loader, "closed", G_CALLBACK(pidgin_conv_custom_smiley_closed), smiley);
-
+	smiley = gtk_imhtml_smiley_create(NULL, smile, FALSE, GTK_IMHTML_SMILEY_CUSTOM);
 	gtk_imhtml_associate_smiley(imhtml, sml, smiley);
+	g_signal_connect_swapped(imhtml, "destroy", G_CALLBACK(gtk_imhtml_smiley_destroy), smiley);
 
 	return TRUE;
 }
@@ -6465,6 +6337,11 @@
 		if(conv->features & PURPLE_CONNECTION_NO_IMAGES)
 			buttons &= ~GTK_IMHTML_IMAGE;
 
+		if (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY)
+			buttons |= GTK_IMHTML_CUSTOM_SMILEY;
+		else
+			buttons &= ~GTK_IMHTML_CUSTOM_SMILEY;
+
 		gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->entry), buttons);
 		if (account != NULL)
 			gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), purple_account_get_protocol_id(account));
@@ -6912,7 +6789,7 @@
 	{
 		PurpleContact *contact = purple_buddy_get_contact(buddy);
 		if (contact) {
-			custom_img = purple_buddy_icons_find_custom_icon(contact);
+			custom_img = purple_buddy_icons_node_find_custom_icon((PurpleBlistNode*)contact);
 			if (custom_img) {
 				/* There is a custom icon for this user */
 				data = purple_imgstore_get_data(custom_img);