changeset 17802:5d4a8b5f9ba3

merge of '371fb91ec4fdb33fa482efe55111fe58d8758c3b' and '9c20b6a1bdeb4ea7f5f83ee92db3988a5796815e'
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Fri, 25 May 2007 22:06:53 +0000
parents 8c056932bcc2 (current diff) 274ceb453176 (diff)
children e83972691531
files
diffstat 25 files changed, 561 insertions(+), 467 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Fri May 25 20:06:37 2007 +0000
+++ b/ChangeLog.API	Fri May 25 22:06:53 2007 +0000
@@ -1,5 +1,23 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.1.0 (?/?/?):
+	Added:
+	* purple_conversation_get_extended_menu
+	* purple_conversation_do_command
+	* pidgin_retrieve_user_info, shows immediate feedback when getting
+	  information about a user.
+	* gtk_imhtml_setup_entry
+
+	Changed:
+	* pidgin_separator returns the separator added to the menu.
+	* pidgin_append_menu_action returns the menuitem added to the menu.
+
+	Signals - Added: (See the Doxygen docs for details on all signals.)
+	* "conversation-extended-menu"
+
+	Finch - Added:
+	* finch_retrieve_user_info
+
 version 2.0.0 (5/3/2007):
 	Please note all functions, defines, and data structures have been
 	re-namespaced to match the new names of Pidgin, Finch, and libpurple.
--- a/doc/conversation-signals.dox	Fri May 25 20:06:37 2007 +0000
+++ b/doc/conversation-signals.dox	Fri May 25 22:06:53 2007 +0000
@@ -29,6 +29,7 @@
   @signal chat-joined
   @signal chat-left
   @signal chat-topic-changed
+  @signal conversation-extended-menu
  @endsignals
 
  @signaldef writing-im-msg
@@ -417,5 +418,15 @@
   @param topic The new topic.
  @endsignaldef
 
+ @signaldef conversation-extended-menu
+  @signalproto
+void (*conversation_extended_menu)(PurpleConversation *conv, GList **list);
+  @endsignalproto
+  @signaldesc
+   Emitted when the UI requests a list of plugin actions for a
+   conversation.
+  @param conv   The conversation.
+  @param list   A pointer to the list of actions.
+ @endsignaldef
 */
 // vim: syntax=c tw=75 et
--- a/finch/gntblist.c	Fri May 25 20:06:37 2007 +0000
+++ b/finch/gntblist.c	Fri May 25 22:06:53 2007 +0000
@@ -824,17 +824,20 @@
 			PURPLE_CALLBACK(finch_add_group), group);
 }
 
+void finch_retrieve_user_info(PurpleConnection *conn, const char *name)
+{
+	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
+	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
+	purple_notify_userinfo(conn, name, info, NULL, NULL);
+	purple_notify_user_info_destroy(info);
+
+	serv_get_info(conn, name);
+}
+
 static void
 finch_blist_get_buddy_info_cb(PurpleBuddy *buddy, PurpleBlistNode *selected)
 {
-	/* Add a userinfo with a "Retrieving information", which will later be updated
-	 * when the server finally returns the information. */
-	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
-	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
-	purple_notify_userinfo(buddy->account->gc, purple_buddy_get_name(buddy), info, NULL, NULL);
-	purple_notify_user_info_destroy(info);
-
-	serv_get_info(buddy->account->gc, purple_buddy_get_name(buddy));
+	finch_retrieve_user_info(buddy->account->gc, purple_buddy_get_name(buddy));
 }
 
 static void
--- a/finch/gntblist.h	Fri May 25 20:06:37 2007 +0000
+++ b/finch/gntblist.h	Fri May 25 22:06:53 2007 +0000
@@ -90,6 +90,14 @@
  */
 void finch_blist_set_size(int width, int height);
 
+/**
+ * Get information about a user. Show immediate feedback.
+ *
+ * @param conn   The connection to get information fro
+ * @param name   The user to get information about.
+ */
+void finch_retrieve_user_info(PurpleConnection *conn, const char *name);
+
 /*@}*/
 
 #endif
--- a/finch/gntconv.c	Fri May 25 20:06:37 2007 +0000
+++ b/finch/gntconv.c	Fri May 25 22:06:53 2007 +0000
@@ -313,12 +313,7 @@
 get_info_cb(GntMenuItem *item, gpointer ggconv)
 {
 	FinchConv *ggc = ggconv;
-	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
-	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
-	purple_notify_userinfo(ggc->active_conv->account->gc, purple_conversation_get_name(ggc->active_conv), info, NULL, NULL);
-	purple_notify_user_info_destroy(info);
-
-	serv_get_info(purple_conversation_get_gc(ggc->active_conv),
+	finch_retrieve_user_info(purple_conversation_get_gc(ggc->active_conv),
 			purple_conversation_get_name(ggc->active_conv));
 }
 
--- a/finch/gntnotify.c	Fri May 25 20:06:37 2007 +0000
+++ b/finch/gntnotify.c	Fri May 25 22:06:53 2007 +0000
@@ -168,7 +168,7 @@
 			gnt_label_new_with_format(_("You have mail!"), GNT_TEXT_FLAG_BOLD));
 
 	emaildialog.tree = tree = gnt_tree_new_with_columns(3);
-	gnt_tree_set_column_titles(GNT_TREE(tree), _("Account"), _("From"), _("Subject"));
+	gnt_tree_set_column_titles(GNT_TREE(tree), _("Account"), _("Sender"), _("Subject"));
 	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
 	gnt_tree_set_col_width(GNT_TREE(tree), 0, 15);
 	gnt_tree_set_col_width(GNT_TREE(tree), 1, 25);
--- a/finch/libgnt/gntmain.c	Fri May 25 20:06:37 2007 +0000
+++ b/finch/libgnt/gntmain.c	Fri May 25 22:06:53 2007 +0000
@@ -359,8 +359,7 @@
 	switch (sig) {
 #ifdef SIGWINCH
 	case SIGWINCH:
-		werase(stdscr);
-		wrefresh(stdscr);
+		erase();
 		g_idle_add(refresh_screen, NULL);
 		org_winch_handler(sig);
 		signal(SIGWINCH, sighandler);
--- a/finch/libgnt/gntwm.c	Fri May 25 20:06:37 2007 +0000
+++ b/finch/libgnt/gntwm.c	Fri May 25 22:06:53 2007 +0000
@@ -980,12 +980,11 @@
 	GntWM *wm = GNT_WM(bindable);
 
 	endwin();
-	refresh();
-	curs_set(0);   /* endwin resets the cursor to normal */
 
 	g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, NULL);
 	update_screen(wm);
 	draw_taskbar(wm, TRUE);
+	curs_set(0);   /* endwin resets the cursor to normal */
 
 	return FALSE;
 }
--- a/libpurple/conversation.c	Fri May 25 20:06:37 2007 +0000
+++ b/libpurple/conversation.c	Fri May 25 22:06:53 2007 +0000
@@ -21,6 +21,7 @@
  */
 #include "internal.h"
 #include "blist.h"
+#include "cmds.h"
 #include "conversation.h"
 #include "dbus-maybe.h"
 #include "debug.h"
@@ -1989,6 +1990,29 @@
 	return cb->name;
 }
 
+GList *
+purple_conversation_get_extended_menu(PurpleConversation *conv)
+{
+	GList *menu = NULL;
+
+	g_return_val_if_fail(conv != NULL, NULL);
+
+	purple_signal_emit(purple_conversations_get_handle(),
+			"conversation-extended-menu", conv, &menu);
+	return menu;
+}
+
+gboolean
+purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline,
+				const gchar *markup, gchar **error)
+{
+	char *mark = (markup && *markup) ? NULL : g_markup_escape_text(cmdline, -1), *err = NULL;
+	PurpleCmdStatus status = purple_cmd_do_command(conv, cmdline, mark ? mark : markup, error ? error : &err);
+	g_free(mark);
+	g_free(err);
+	return (status == PURPLE_CMD_STATUS_OK);
+}
+
 void *
 purple_conversations_get_handle(void)
 {
@@ -2252,6 +2276,12 @@
 										PURPLE_SUBTYPE_CONVERSATION),
 						 purple_value_new(PURPLE_TYPE_STRING),
 						 purple_value_new(PURPLE_TYPE_STRING));
+
+	purple_signal_register(handle, "conversation-extended-menu",
+			     purple_marshal_VOID__POINTER_POINTER, NULL, 2,
+			     purple_value_new(PURPLE_TYPE_SUBTYPE,
+					    PURPLE_SUBTYPE_CONVERSATION),
+			     purple_value_new(PURPLE_TYPE_BOXED, "GList **"));
 }
 
 void
--- a/libpurple/conversation.h	Fri May 25 20:06:37 2007 +0000
+++ b/libpurple/conversation.h	Fri May 25 22:06:53 2007 +0000
@@ -1190,6 +1190,30 @@
  */
 void purple_conv_chat_cb_destroy(PurpleConvChatBuddy *cb);
 
+/**
+ * Retrieves the extended menu items for the conversation.
+ *
+ * @param conv The conversation.
+ * 
+ * @return  A list of PurpleMenuAction items, harvested by the
+ *          chat-extended-menu signal. The list and the menuaction
+ *          items should be freed by the caller.
+ */
+GList * purple_conversation_get_extended_menu(PurpleConversation *conv);
+
+/**
+ * Perform a command in a conversation. Similar to @see purple_cmd_do_command
+ *
+ * @param conv    The conversation.
+ * @param cmdline The entire command including the arguments.
+ * @param markup  @c NULL, or the formatted command line.
+ * @param error   If the command failed errormsg is filled in with the appropriate error
+ *                message, if not @c NULL. It must be freed by the caller with g_free().
+ *
+ * @return  @c TRUE if the command was executed successfully, @c FALSE otherwise.
+ */
+gboolean purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline, const gchar *markup, gchar **error);
+
 /*@}*/
 
 /**************************************************************************/
--- a/libpurple/imgstore.c	Fri May 25 20:06:37 2007 +0000
+++ b/libpurple/imgstore.c	Fri May 25 22:06:53 2007 +0000
@@ -25,6 +25,7 @@
 */
 
 #include <glib.h>
+#include "dbus-maybe.h"
 #include "debug.h"
 #include "imgstore.h"
 #include "util.h"
@@ -56,6 +57,7 @@
 	g_return_val_if_fail(size > 0, 0);
 
 	img = g_new(PurpleStoredImage, 1);
+	PURPLE_DBUS_REGISTER_POINTER(img, PurpleStoredImage);
 	img->data = data;
 	img->size = size;
 	img->filename = g_strdup(filename);
@@ -159,6 +161,7 @@
 
 		g_free(img->data);
 		g_free(img->filename);
+		PURPLE_DBUS_UNREGISTER_POINTER(img);
 		g_free(img);
 		img = NULL;
 	}
--- a/libpurple/plugins/perl/common/Account.xs	Fri May 25 20:06:37 2007 +0000
+++ b/libpurple/plugins/perl/common/Account.xs	Fri May 25 22:06:53 2007 +0000
@@ -215,6 +215,7 @@
         t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(list), i, 0), t_sl));
     }
     purple_account_add_buddies(account, t_GL);
+    g_list_free(t_GL);
 
 void
 purple_account_add_buddy(account, buddy)
@@ -252,6 +253,8 @@
         t_GL2 = g_list_append(t_GL2, SvPV(*av_fetch((AV *)SvRV(B), i, 0), t_sl));
     }
     purple_account_remove_buddies(account, t_GL1, t_GL2);
+    g_list_free(t_GL1);
+    g_list_free(t_GL2);
 
 void
 purple_account_remove_buddy(account, buddy, group)
--- a/libpurple/plugins/perl/common/BuddyList.xs	Fri May 25 20:06:37 2007 +0000
+++ b/libpurple/plugins/perl/common/BuddyList.xs	Fri May 25 22:06:53 2007 +0000
@@ -263,7 +263,7 @@
 PREINIT:
 	GList *l;
 PPCODE:
-	for (l = purple_blist_node_get_extended_menu(node); l != NULL; l = l->next) {
+	for (l = purple_blist_node_get_extended_menu(node); l != NULL; l = g_list_delete_link(l, l)) {
 		XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Menu::Action")));
 	}
 
--- a/libpurple/plugins/perl/common/Conversation.xs	Fri May 25 20:06:37 2007 +0000
+++ b/libpurple/plugins/perl/common/Conversation.xs	Fri May 25 22:06:53 2007 +0000
@@ -218,6 +218,21 @@
 	Purple::Conversation conv
 	Purple::Account account
 
+void
+purple_conversation_write(conv, who, message, flags, mtime)
+	Purple::Conversation conv
+	const char *who
+	const char *message
+	Purple::MessageFlags flags
+	time_t mtime
+
+gboolean
+purple_conversation_do_command(conv, cmdline, markup, error)
+	Purple::Conversation conv
+	const char *cmdline
+	const char *markup
+	char **error
+
 MODULE = Purple::Conversation  PACKAGE = Purple::Conversation::IM  PREFIX = purple_conv_im_
 PROTOTYPES: ENABLE
 
--- a/libpurple/plugins/perl/common/Prefs.xs	Fri May 25 20:06:37 2007 +0000
+++ b/libpurple/plugins/perl/common/Prefs.xs	Fri May 25 22:06:53 2007 +0000
@@ -94,8 +94,9 @@
 PREINIT:
 	GList *l;
 PPCODE:
-	for (l = purple_prefs_get_string_list(name); l != NULL; l = l->next) {
-		XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::PrefValue")));
+	for (l = purple_prefs_get_string_list(name); l != NULL; l = g_list_delete_link(l, l)) {
+		XPUSHs(sv_2mortal(newSVpv(l->data, 0)));
+		g_free(l->data);
 	}
 
 Purple::PrefType
--- a/pidgin/gtkblist.c	Fri May 25 20:06:37 2007 +0000
+++ b/pidgin/gtkblist.c	Fri May 25 22:06:53 2007 +0000
@@ -274,12 +274,7 @@
 
 static void gtk_blist_menu_info_cb(GtkWidget *w, PurpleBuddy *b)
 {
-	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
-	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
-	purple_notify_userinfo(b->account->gc, purple_buddy_get_name(b), info, NULL, NULL);
-	purple_notify_user_info_destroy(info);
-
-	serv_get_info(b->account->gc, b->name);
+	pidgin_retrieve_user_info(b->account->gc, purple_buddy_get_name(b));
 }
 
 static void gtk_blist_menu_im_cb(GtkWidget *w, PurpleBuddy *b)
@@ -1147,7 +1142,8 @@
 }
 
 static gboolean
-gtk_blist_key_press_cb(GtkWidget *tv, GdkEventKey *event, gpointer data) {
+gtk_blist_key_press_cb(GtkWidget *tv, GdkEventKey *event, gpointer data)
+{
 	PurpleBlistNode *node;
 	GValue val;
 	GtkTreeIter iter;
@@ -1174,7 +1170,7 @@
 			return FALSE;
 		}
 		if(buddy)
-			serv_get_info(buddy->account->gc, buddy->name);
+			pidgin_retrieve_user_info(buddy->account->gc, buddy->name);
 	} else if (event->keyval == GDK_F2) {
 		gtk_blist_menu_alias_cb(tv, node);
 	}
@@ -1428,7 +1424,7 @@
 			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
 		if (prpl && prpl_info->get_info)
-			serv_get_info(b->account->gc, b->name);
+			pidgin_retrieve_user_info(b->account->gc, b->name);
 		handled = TRUE;
 	}
 
--- a/pidgin/gtkconv.c	Fri May 25 20:06:37 2007 +0000
+++ b/pidgin/gtkconv.c	Fri May 25 22:06:53 2007 +0000
@@ -267,65 +267,7 @@
 default_formatize(PidginConversation *c)
 {
 	PurpleConversation *conv = c->active_conv;
-
-	if (conv->features & PURPLE_CONNECTION_HTML)
-	{
-		char color[8];
-		GdkColor fg_color, bg_color;
-
-		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold") != GTK_IMHTML(c->entry)->edit.bold)
-			gtk_imhtml_toggle_bold(GTK_IMHTML(c->entry));
-
-		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic") != GTK_IMHTML(c->entry)->edit.italic)
-			gtk_imhtml_toggle_italic(GTK_IMHTML(c->entry));
-
-		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline") != GTK_IMHTML(c->entry)->edit.underline)
-			gtk_imhtml_toggle_underline(GTK_IMHTML(c->entry));
-
-		gtk_imhtml_toggle_fontface(GTK_IMHTML(c->entry),
-			purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face"));
-
-		if (!(conv->features & PURPLE_CONNECTION_NO_FONTSIZE))
-		{
-			int size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size");
-
-			/* 3 is the default. */
-			if (size != 3)
-				gtk_imhtml_font_set_size(GTK_IMHTML(c->entry), size);
-		}
-
-		if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), "") != 0)
-		{
-			gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"),
-							&fg_color);
-			g_snprintf(color, sizeof(color), "#%02x%02x%02x",
-									fg_color.red   / 256,
-									fg_color.green / 256,
-									fg_color.blue  / 256);
-		} else
-			strcpy(color, "");
-
-		gtk_imhtml_toggle_forecolor(GTK_IMHTML(c->entry), color);
-
-		if(!(conv->features & PURPLE_CONNECTION_NO_BGCOLOR) &&
-		   strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), "") != 0)
-		{
-			gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"),
-							&bg_color);
-			g_snprintf(color, sizeof(color), "#%02x%02x%02x",
-									bg_color.red   / 256,
-									bg_color.green / 256,
-									bg_color.blue  / 256);
-		} else
-			strcpy(color, "");
-
-		gtk_imhtml_toggle_background(GTK_IMHTML(c->entry), color);
-
-		if (conv->features & PURPLE_CONNECTION_FORMATTING_WBFO)
-			gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(c->entry), TRUE);
-		else
-			gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(c->entry), FALSE);
-	}
+	gtk_imhtml_setup_entry(GTK_IMHTML(c->entry), conv->features);
 }
 
 static void
@@ -673,14 +615,8 @@
 	PurpleConversation *conv = gtkconv->active_conv;
 
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
-		PurpleNotifyUserInfo *info = purple_notify_user_info_new();
-		purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
-		purple_notify_userinfo(conv->account->gc, purple_conversation_get_name(conv), info, NULL, NULL);
-		purple_notify_user_info_destroy(info);
-
-		serv_get_info(purple_conversation_get_gc(conv),
+		pidgin_retrieve_user_info(purple_conversation_get_gc(conv),
 					  purple_conversation_get_name(conv));
-
 		gtk_widget_grab_focus(gtkconv->entry);
 	} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 		/* Get info of the person currently selected in the GtkTreeView */
@@ -2224,34 +2160,18 @@
 static GList *get_prpl_icon_list(PurpleAccount *account)
 {
 	GList *l = NULL;
-	GdkPixbuf *pixbuf;
-	PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account)));
-	const char *prpl = prpl_info->list_icon(account, NULL);
-	char *filename, *path;
-	l = g_hash_table_lookup(prpl_lists, prpl);
+	PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account));
+	PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+	const char *prplname = prpl_info->list_icon(account, NULL);
+	l = g_hash_table_lookup(prpl_lists, prplname);
 	if (l)
 		return l;
-	filename = g_strdup_printf("%s.png", prpl);
-
-	path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "16", filename, NULL);
-	pixbuf = gdk_pixbuf_new_from_file(path, NULL);
-	if (pixbuf)
-		l = g_list_append(l, pixbuf);
-	g_free(path);
-
-	path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", filename, NULL);
-        pixbuf = gdk_pixbuf_new_from_file(path, NULL);
-        if (pixbuf)
-                l = g_list_append(l, pixbuf);
-        g_free(path);
-
-	path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "48", filename, NULL);
-        pixbuf = gdk_pixbuf_new_from_file(path, NULL);
-        if (pixbuf)
-                l = g_list_append(l, pixbuf);
-        g_free(path);
-
-	g_hash_table_insert(prpl_lists, g_strdup(prpl), l);
+
+	l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_LARGE));
+	l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM));
+	l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL));
+
+	g_hash_table_insert(prpl_lists, g_strdup(prplname), l);
 	return l;
 }
 
@@ -2928,10 +2848,65 @@
 	gtk_widget_show_all(menu);
 }
 
+static void
+remove_from_list(GtkWidget *widget, PidginWindow *win)
+{
+	GList *list = g_object_get_data(G_OBJECT(win->window), "plugin-actions");
+	list = g_list_remove(list, widget);
+	g_object_set_data(G_OBJECT(win->window), "plugin-actions", list);
+}
+
+static void
+regenerate_plugins_items(PidginWindow *win)
+{
+	GList *action_items;
+	GtkWidget *menu;
+	GList *list;
+	PidginConversation *gtkconv;
+	PurpleConversation *conv;
+	GtkWidget *item;
+
+	if (win->window == NULL || win == hidden_convwin)
+		return;
+
+	gtkconv = pidgin_conv_window_get_active_gtkconv(win);
+	if (gtkconv == NULL)
+		return;
+
+	conv = gtkconv->active_conv;
+	action_items = g_object_get_data(G_OBJECT(win->window), "plugin-actions");
+
+	/* Remove the old menuitems */
+	while (action_items) {
+		g_signal_handlers_disconnect_by_func(G_OBJECT(action_items->data),
+					G_CALLBACK(remove_from_list), win);
+		gtk_widget_destroy(action_items->data);
+		action_items = g_list_delete_link(action_items, action_items);
+	}
+
+	menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Options"));
+
+	list = purple_conversation_get_extended_menu(conv);
+	if (list) {
+		action_items = g_list_prepend(NULL, (item = pidgin_separator(menu)));
+		g_signal_connect(G_OBJECT(item), "destroy", G_CALLBACK(remove_from_list), win);
+	}
+
+	for(; list; list = g_list_delete_link(list, list)) {
+		PurpleMenuAction *act = (PurpleMenuAction *) list->data;
+		item = pidgin_append_menu_action(menu, act, conv);
+		action_items = g_list_prepend(action_items, item);
+		gtk_widget_show_all(item);
+		g_signal_connect(G_OBJECT(item), "destroy", G_CALLBACK(remove_from_list), win);
+	}
+	g_object_set_data(G_OBJECT(win->window), "plugin-actions", action_items);
+}
+
 static void menubar_activated(GtkWidget *item, gpointer data)
 {
 	PidginWindow *win = data;
 	regenerate_options_items(win);
+	regenerate_plugins_items(win);
 
 	/* The following are to make sure the 'More' submenu is not regenerated every time
 	 * the focus shifts from 'Conversations' to some other menu and back. */
@@ -4131,45 +4106,17 @@
 	}
 }
 
-static GtkWidget *
-setup_chat_pane(PidginConversation *gtkconv)
-{
-	PurplePluginProtocolInfo *prpl_info;
+static void
+setup_chat_topic(PidginConversation *gtkconv, GtkWidget *vbox)
+{
 	PurpleConversation *conv = gtkconv->active_conv;
-	PidginChatPane *gtkchat;
-	PurpleConnection *gc;
-	GtkWidget *vpaned, *hpaned;
-	GtkWidget *vbox, *hbox, *frame;
-	GtkWidget *imhtml_sw;
-	GtkPolicyType imhtml_sw_hscroll;
-	GtkWidget *lbox;
-	GtkWidget *label;
-	GtkWidget *list;
-	GtkWidget *sw;
-	GtkListStore *ls;
-	GtkCellRenderer *rend;
-	GtkTreeViewColumn *col;
-	void *blist_handle = purple_blist_get_handle();
-	GList *focus_chain = NULL;
-	int ul_width;
-
-	gtkchat = gtkconv->u.chat;
-	gc      = purple_conversation_get_gc(conv);
-	g_return_val_if_fail(gc != NULL, NULL);
-	g_return_val_if_fail(gc->prpl != NULL, NULL);
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
-
-	/* Setup the outer pane. */
-	vpaned = gtk_vpaned_new();
-	gtk_widget_show(vpaned);
-
-	/* Setup the top part of the pane. */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_paned_pack1(GTK_PANED(vpaned), vbox, TRUE, TRUE);
-	gtk_widget_show(vbox);
-
+	PurpleConnection *gc = purple_conversation_get_gc(conv);
+	PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
 	if (prpl_info->options & OPT_PROTO_CHAT_TOPIC)
 	{
+		GtkWidget *hbox, *label;
+		PidginChatPane *gtkchat = gtkconv->u.chat;
+		
 		hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
 		gtk_widget_show(hbox);
@@ -4190,35 +4137,19 @@
 		gtk_box_pack_start(GTK_BOX(hbox), gtkchat->topic_text, TRUE, TRUE, 0);
 		gtk_widget_show(gtkchat->topic_text);
 	}
-
-	/* Setup the horizontal pane. */
-	hpaned = gtk_hpaned_new();
-	gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
-	gtk_widget_show(hpaned);
-
-	/* Setup gtkihmtml. */
-	frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw);
-	gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml");
-	gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml), TRUE);
-	gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE);
-	gtk_widget_show(frame);
-	gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
-	                               &imhtml_sw_hscroll, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
-	                               imhtml_sw_hscroll, GTK_POLICY_ALWAYS);
-
-	gtk_widget_set_size_request(gtkconv->imhtml,
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width"),
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height"));
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate",
-					 G_CALLBACK(size_allocate_cb), gtkconv);
-
-	g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
-						   G_CALLBACK(entry_stop_rclick_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
-						   G_CALLBACK(refocus_entry_cb), gtkconv);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
-						   G_CALLBACK(refocus_entry_cb), gtkconv);
+}
+
+static void
+setup_chat_userlist(PidginConversation *gtkconv, GtkWidget *hpaned)
+{
+	PidginChatPane *gtkchat = gtkconv->u.chat;
+	GtkWidget *lbox, *sw, *list;
+	GtkListStore *ls;
+	GtkCellRenderer *rend;
+	GtkTreeViewColumn *col;
+	int ul_width;
+	void *blist_handle = purple_blist_get_handle();
+	PurpleConversation *conv = gtkconv->active_conv;
 
 	/* Build the right pane. */
 	lbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -4266,9 +4197,7 @@
 			 G_CALLBACK(gtkconv_chat_popup_menu_cb), gtkconv);
 	g_signal_connect(G_OBJECT(lbox), "size-allocate", G_CALLBACK(lbox_size_allocate_cb), gtkconv);
 
-
 	rend = gtk_cell_renderer_text_new();
-
 	g_object_set(rend,
 				 "foreground-set", TRUE,
 				 "weight-set", TRUE,
@@ -4299,10 +4228,72 @@
 	gtkchat->list = list;
 
 	gtk_container_add(GTK_CONTAINER(sw), list);
+}
+
+static GtkWidget *
+setup_common_pane(PidginConversation *gtkconv)
+{
+	GtkWidget *paned, *vbox, *frame, *imhtml_sw;
+	PurpleConversation *conv = gtkconv->active_conv;
+	gboolean chat = (conv->type == PURPLE_CONV_TYPE_CHAT);
+	GtkPolicyType imhtml_sw_hscroll;
+
+	paned = gtk_vpaned_new();
+	gtk_widget_show(paned);
+
+	/* Setup the top part of the pane */
+	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	gtk_paned_pack1(GTK_PANED(paned), vbox, TRUE, TRUE);
+	gtk_widget_show(vbox);
+
+	/* Setup the gtkimhtml widget */
+	frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw);
+	if (chat) {
+		GtkWidget *hpaned;
+
+		/* Add the topic */
+		setup_chat_topic(gtkconv, vbox);
+
+		/* Add the gtkimhtml frame */
+		hpaned = gtk_hpaned_new();
+		gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
+		gtk_widget_show(hpaned);
+		gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE);
+
+		/* Now add the userlist */
+		setup_chat_userlist(gtkconv, hpaned);
+	} else {
+		gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
+	}
+	gtk_widget_show(frame);
+
+	gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml");
+	gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE);
+
+	gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
+	                               &imhtml_sw_hscroll, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
+	                               imhtml_sw_hscroll, GTK_POLICY_ALWAYS);
+
+	gtk_widget_set_size_request(gtkconv->imhtml,
+			chat ? purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width") :
+			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width"),
+			chat ? purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height") :
+			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height"));
+
+	g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate",
+	                 G_CALLBACK(size_allocate_cb), gtkconv);
+
+	g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
+	                       G_CALLBACK(entry_stop_rclick_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
+	                 G_CALLBACK(refocus_entry_cb), gtkconv);
+	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
+	                 G_CALLBACK(refocus_entry_cb), gtkconv);
 
 	/* Setup the bottom half of the conversation window */
 	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_paned_pack2(GTK_PANED(vpaned), vbox, FALSE, TRUE);
+	gtk_paned_pack2(GTK_PANED(paned), vbox, FALSE, TRUE);
 	gtk_widget_show(vbox);
 
 	gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -4318,20 +4309,15 @@
 	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 	gtk_widget_show(frame);
 
-	g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
-					 G_CALLBACK(entry_popup_menu_cb), gtkconv);
-
 	gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry");
 	gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry),
-								 purple_account_get_protocol_name(conv->account));
+			purple_account_get_protocol_name(conv->account));
 	gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height"));
-	gtkconv->entry_buffer =
-		gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
-	g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv);
-	g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed",
-                                 G_CALLBACK(resize_imhtml_cb), gtkconv);
-
+			chat ? purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height") :
+			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height"));
+
+	g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
+	                 G_CALLBACK(entry_popup_menu_cb), gtkconv);
 	g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event",
 	                 G_CALLBACK(entry_key_press_cb), gtkconv);
 	g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send",
@@ -4341,129 +4327,28 @@
 	g_signal_connect(G_OBJECT(gtkconv->lower_hbox), "size-allocate",
 	                 G_CALLBACK(size_allocate_cb), gtkconv);
 
-	default_formatize(gtkconv);
-
-	/*
-	 * Focus for chat windows should be as follows:
-	 * Tab title -> chat topic -> conversation scrollback -> user list ->
-	 *   user list buttons -> entry -> buttons at bottom
-	 */
-	focus_chain = g_list_prepend(focus_chain, gtkconv->entry);
-	gtk_container_set_focus_chain(GTK_CONTAINER(vbox), focus_chain);
-
-	return vpaned;
-}
-
-static GtkWidget *
-setup_im_pane(PidginConversation *gtkconv)
-{
-	PurpleConversation *conv = gtkconv->active_conv;
-	GtkWidget *frame;
-	GtkWidget *imhtml_sw;
-	GtkPolicyType imhtml_sw_hscroll;
-	GtkWidget *paned;
-	GtkWidget *vbox;
-	GtkWidget *vbox2;
-	GList *focus_chain = NULL;
-
-	/* Setup the outer pane */
-	paned = gtk_vpaned_new();
-	gtk_widget_show(paned);
-
-	/* Setup the top part of the pane */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_paned_pack1(GTK_PANED(paned), vbox, TRUE, TRUE);
-	gtk_widget_show(vbox);
-
-	/* Setup the gtkimhtml widget */
-	frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw);
-	gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml");
-	gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE);
-	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
-	gtk_widget_show(frame);
-	gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
-	                               &imhtml_sw_hscroll, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
-	                               imhtml_sw_hscroll, GTK_POLICY_ALWAYS);
-
-	gtk_widget_set_size_request(gtkconv->imhtml,
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width"),
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height"));
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate",
-	                 G_CALLBACK(size_allocate_cb), gtkconv);
-
-	g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
-	                       G_CALLBACK(entry_stop_rclick_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
-	                 G_CALLBACK(refocus_entry_cb), gtkconv);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
-	                 G_CALLBACK(refocus_entry_cb), gtkconv);
-
-	/* Setup the bottom half of the conversation window */
-	vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_paned_pack2(GTK_PANED(paned), vbox2, FALSE, TRUE);
-	gtk_widget_show(vbox2);
-
-	gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox2), gtkconv->lower_hbox, TRUE, TRUE, 0);
-	gtk_widget_show(gtkconv->lower_hbox);
-
-	vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_end(GTK_BOX(gtkconv->lower_hbox), vbox2, TRUE, TRUE, 0);
-	gtk_widget_show(vbox2);
-
-	/* Setup the toolbar, entry widget and all signals */
-	frame = pidgin_create_imhtml(TRUE, &gtkconv->entry, &gtkconv->toolbar, NULL);
-	gtk_box_pack_start(GTK_BOX(vbox2), frame, TRUE, TRUE, 0);
-	gtk_widget_show(frame);
-
-	g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
-					 G_CALLBACK(entry_popup_menu_cb), gtkconv);
-
-	gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry");
-	gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry),
-								 purple_account_get_protocol_name(conv->account));
-	gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height"));
 	gtkconv->entry_buffer =
 		gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
 	g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv);
 
-	g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event",
-	                 G_CALLBACK(entry_key_press_cb), gtkconv);
-	g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send",
-	                       G_CALLBACK(send_cb), gtkconv);
-	g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event",
-	                       G_CALLBACK(entry_stop_rclick_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkconv->lower_hbox), "size-allocate",
-	                 G_CALLBACK(size_allocate_cb), gtkconv);
-
-	g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text",
-	                 G_CALLBACK(insert_text_cb), gtkconv);
-	g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range",
-	                 G_CALLBACK(delete_text_cb), gtkconv);
+	if (!chat) {
+		/* For sending typing notifications for IMs */
+		g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text",
+						 G_CALLBACK(insert_text_cb), gtkconv);
+		g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range",
+						 G_CALLBACK(delete_text_cb), gtkconv);
+		gtkconv->u.im->typing_timer = 0;
+		gtkconv->u.im->animate = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons");
+		gtkconv->u.im->show_icon = TRUE;
+	}
+
 	g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed",
 				 G_CALLBACK(resize_imhtml_cb), gtkconv);
 
-	/* had to move this after the imtoolbar is attached so that the
-	 * signals get fired to toggle the buttons on the toolbar as well.
-	 */
 	default_formatize(gtkconv);
-
 	g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear",
-						   G_CALLBACK(clear_formatting_cb), gtkconv);
-
-	gtkconv->u.im->animate = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons");
-	gtkconv->u.im->show_icon = TRUE;
-
-	/*
-	 * Focus for IM windows should be as follows:
-	 * Tab title -> conversation scrollback -> entry
-	 */
-	focus_chain = g_list_prepend(focus_chain, gtkconv->entry);
-	gtk_container_set_focus_chain(GTK_CONTAINER(vbox2), focus_chain);
-
-	gtkconv->u.im->typing_timer = 0;
+	                       G_CALLBACK(clear_formatting_cb), gtkconv);
+
 	return paned;
 }
 
@@ -4659,12 +4544,10 @@
 
 	if (conv_type == PURPLE_CONV_TYPE_IM) {
 		gtkconv->u.im = g_malloc0(sizeof(PidginImPane));
-
-		pane = setup_im_pane(gtkconv);
 	} else if (conv_type == PURPLE_CONV_TYPE_CHAT) {
 		gtkconv->u.chat = g_malloc0(sizeof(PidginChatPane));
-		pane = setup_chat_pane(gtkconv);
-	}
+	}
+	pane = setup_common_pane(gtkconv);
 
 	gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->imhtml),
 			gtk_imhtml_get_format_functions(GTK_IMHTML(gtkconv->imhtml)) | GTK_IMHTML_IMAGE);
@@ -7964,6 +7847,7 @@
 
 	generate_send_to_items(win);
 	regenerate_options_items(win);
+	regenerate_plugins_items(win);
 
 	pidgin_conv_switch_active_conversation(conv);
 
@@ -8034,6 +7918,12 @@
 	prpl_lists = g_hash_table_new(g_str_hash, g_str_equal);
 }
 
+static void
+plugin_changed_cb(PurplePlugin *p, gpointer data)
+{
+	regenerate_plugins_items(data);
+}
+
 PidginWindow *
 pidgin_conv_window_new()
 {
@@ -8108,6 +7998,13 @@
 
 	gtk_widget_show(testidea);
 
+	/* Update the plugin actions when plugins are (un)loaded */
+	purple_signal_connect(purple_plugins_get_handle(), "plugin-load",
+			win, PURPLE_CALLBACK(plugin_changed_cb), win);
+	purple_signal_connect(purple_plugins_get_handle(), "plugin-unload",
+			win, PURPLE_CALLBACK(plugin_changed_cb), win);
+
+
 #ifdef _WIN32
 	g_signal_connect(G_OBJECT(win->window), "show",
 	                 G_CALLBACK(winpidgin_ensure_onscreen), win->window);
@@ -8147,6 +8044,7 @@
 	g_object_unref(G_OBJECT(win->menu.item_factory));
 
 	purple_notify_close_with_handle(win);
+	purple_signals_disconnect_by_handle(win);
 
 	g_free(win);
 }
--- a/pidgin/gtkdialogs.c	Fri May 25 20:06:37 2007 +0000
+++ b/pidgin/gtkdialogs.c	Fri May 25 22:06:53 2007 +0000
@@ -823,7 +823,7 @@
 		found = pidgin_dialogs_ee(username);
 
 	if (!found && username != NULL && *username != '\0' && account != NULL)
-		serv_get_info(purple_account_get_connection(account), username);
+		pidgin_retrieve_user_info(purple_account_get_connection(account), username);
 
 	g_free(username);
 }
--- a/pidgin/gtkimhtml.c	Fri May 25 20:06:37 2007 +0000
+++ b/pidgin/gtkimhtml.c	Fri May 25 22:06:53 2007 +0000
@@ -31,6 +31,7 @@
 #include "util.h"
 #include "gtkimhtml.h"
 #include "gtksourceiter.h"
+#include "pidgin.h"
 #include <gtk/gtk.h>
 #include <glib/gerror.h>
 #include <gdk/gdkkeysyms.h>
@@ -4856,3 +4857,69 @@
 	g_return_if_fail(imhtml != NULL);
 	imhtml->funcs = f;
 }
+
+void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags)
+{
+	if (flags & PURPLE_CONNECTION_HTML) {
+		char color[8];
+		GdkColor fg_color, bg_color;
+
+		gtk_imhtml_set_format_functions(imhtml, GTK_IMHTML_ALL);
+		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold") != imhtml->edit.bold)
+			gtk_imhtml_toggle_bold(imhtml);
+
+		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic") != imhtml->edit.italic)
+			gtk_imhtml_toggle_italic(imhtml);
+
+		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline") != imhtml->edit.underline)
+			gtk_imhtml_toggle_underline(imhtml);
+
+		gtk_imhtml_toggle_fontface(imhtml,
+			purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face"));
+
+		if (!(flags & PURPLE_CONNECTION_NO_FONTSIZE))
+		{
+			int size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size");
+
+			/* 3 is the default. */
+			if (size != 3)
+				gtk_imhtml_font_set_size(imhtml, size);
+		}
+
+		if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), "") != 0)
+		{
+			gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"),
+							&fg_color);
+			g_snprintf(color, sizeof(color), "#%02x%02x%02x",
+									fg_color.red   / 256,
+									fg_color.green / 256,
+									fg_color.blue  / 256);
+		} else
+			strcpy(color, "");
+
+		gtk_imhtml_toggle_forecolor(imhtml, color);
+
+		if(!(flags & PURPLE_CONNECTION_NO_BGCOLOR) &&
+		   strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), "") != 0)
+		{
+			gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"),
+							&bg_color);
+			g_snprintf(color, sizeof(color), "#%02x%02x%02x",
+									bg_color.red   / 256,
+									bg_color.green / 256,
+									bg_color.blue  / 256);
+		} else
+			strcpy(color, "");
+
+		gtk_imhtml_toggle_background(imhtml, color);
+
+		if (flags & PURPLE_CONNECTION_FORMATTING_WBFO)
+			gtk_imhtml_set_whole_buffer_formatting_only(imhtml, TRUE);
+		else
+			gtk_imhtml_set_whole_buffer_formatting_only(imhtml, FALSE);
+	} else {
+		imhtml_clear_formatting(imhtml);
+		gtk_imhtml_set_format_functions(imhtml, 0);
+	}
+}
+
--- a/pidgin/gtkimhtml.h	Fri May 25 20:06:37 2007 +0000
+++ b/pidgin/gtkimhtml.h	Fri May 25 22:06:53 2007 +0000
@@ -28,6 +28,8 @@
 #include <gtk/gtktooltips.h>
 #include <gtk/gtkimage.h>
 
+#include "connection.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -786,6 +788,14 @@
  */
 char *gtk_imhtml_get_text(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *stop);
 
+/**
+ * Setup formatting for an imhtml depending on the flags specified.
+ *
+ * @param imhtml  The GTK+ IM/HTML.
+ * @param flags   The connection flag which describes the allowed types of formatting.
+ */
+void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags);
+
 /*@}*/
 
 #ifdef __cplusplus
--- a/pidgin/gtkpounce.c	Fri May 25 20:06:37 2007 +0000
+++ b/pidgin/gtkpounce.c	Fri May 25 22:06:53 2007 +0000
@@ -38,6 +38,7 @@
 
 #include "gtkblist.h"
 #include "gtkdialogs.h"
+#include "gtkimhtml.h"
 #include "gtkpounce.h"
 #include "pidginstock.h"
 #include "gtkutils.h"
@@ -241,7 +242,8 @@
 save_pounce_cb(GtkWidget *w, PidginPounceDialog *dialog)
 {
 	const char *name;
-	const char *message, *command, *sound, *reason;
+	const char *command, *sound, *reason;
+	char *message;
 	PurplePounceEvent events   = PURPLE_POUNCE_NONE;
 	PurplePounceOption options = PURPLE_POUNCE_OPTION_NONE;
 
@@ -290,13 +292,16 @@
 		events |= PURPLE_POUNCE_MESSAGE_RECEIVED;
 
 	/* Data fields */
-	message = gtk_entry_get_text(GTK_ENTRY(dialog->send_msg_entry));
+	message = gtk_imhtml_get_markup(GTK_IMHTML(dialog->send_msg_entry));
 	command = gtk_entry_get_text(GTK_ENTRY(dialog->exec_cmd_entry));
 	sound   = gtk_entry_get_text(GTK_ENTRY(dialog->play_sound_entry));
 	reason  = gtk_entry_get_text(GTK_ENTRY(dialog->popup_entry));
 
 	if (*reason == '\0') reason = NULL;
-	if (*message == '\0') message = NULL;
+	if (*message == '\0') {
+		g_free(message);
+		message = NULL;
+	}
 	if (*command == '\0') command = NULL;
 	if (*sound   == '\0') sound   = NULL;
 
@@ -349,6 +354,7 @@
 		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->save_pounce)));
 
 	update_pounces();
+	g_free(message);
 
 	delete_win_cb(NULL, NULL, dialog);
 }
@@ -446,6 +452,14 @@
 	{"application/x-im-contact", 0, 1}
 };
 
+static void
+reset_send_msg_entry(PidginPounceDialog *dialog, GtkWidget *dontcare)
+{
+	PurpleAccount *account = pidgin_account_option_menu_get_selected(dialog->account_menu);
+	gtk_imhtml_setup_entry(GTK_IMHTML(dialog->send_msg_entry),
+			(account && account->gc) ? account->gc->flags : PURPLE_CONNECTION_HTML);
+}
+
 void
 pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
 							PurplePounce *cur_pounce)
@@ -462,6 +476,7 @@
 	GtkSizeGroup *sg;
 	GPtrArray *sound_widgets;
 	GPtrArray *exec_widgets;
+	GtkWidget *send_msg_imhtml;
 
 	g_return_if_fail((cur_pounce != NULL) ||
 	                 (account != NULL) ||
@@ -653,7 +668,8 @@
 	dialog->play_sound
 		= gtk_check_button_new_with_mnemonic(_("P_lay a sound"));
 
-	dialog->send_msg_entry    = gtk_entry_new();
+	send_msg_imhtml = pidgin_create_imhtml(TRUE, &dialog->send_msg_entry, NULL, NULL);
+	reset_send_msg_entry(dialog, NULL);
 	dialog->exec_cmd_entry    = gtk_entry_new();
 	dialog->popup_entry       = gtk_entry_new();
 	dialog->exec_cmd_browse   = gtk_button_new_with_mnemonic(_("Brows_e..."));
@@ -661,7 +677,7 @@
 	dialog->play_sound_browse = gtk_button_new_with_mnemonic(_("Br_owse..."));
 	dialog->play_sound_test   = gtk_button_new_with_mnemonic(_("Pre_view"));
 
-	gtk_widget_set_sensitive(dialog->send_msg_entry,    FALSE);
+	gtk_widget_set_sensitive(send_msg_imhtml,           FALSE);
 	gtk_widget_set_sensitive(dialog->exec_cmd_entry,    FALSE);
 	gtk_widget_set_sensitive(dialog->popup_entry,       FALSE);
 	gtk_widget_set_sensitive(dialog->exec_cmd_browse,   FALSE);
@@ -673,8 +689,6 @@
 	gtk_size_group_add_widget(sg, dialog->open_win);
 	gtk_size_group_add_widget(sg, dialog->popup);
 	gtk_size_group_add_widget(sg, dialog->popup_entry);
-	gtk_size_group_add_widget(sg, dialog->send_msg);
-	gtk_size_group_add_widget(sg, dialog->send_msg_entry);
 	gtk_size_group_add_widget(sg, dialog->exec_cmd);
 	gtk_size_group_add_widget(sg, dialog->exec_cmd_entry);
 	gtk_size_group_add_widget(sg, dialog->exec_cmd_browse);
@@ -689,23 +703,23 @@
 					 GTK_FILL, 0, 0, 0);
 	gtk_table_attach(GTK_TABLE(table), dialog->popup_entry,      1, 4, 1, 2,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->send_msg,         0, 1, 2, 3,
+	gtk_table_attach(GTK_TABLE(table), dialog->send_msg,         0, 4, 2, 3,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->send_msg_entry,   1, 4, 2, 3,
+	gtk_table_attach(GTK_TABLE(table), send_msg_imhtml,          0, 4, 3, 4,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd,         0, 1, 3, 4,
+	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd,         0, 1, 4, 5,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd_entry,   1, 2, 3, 4,
+	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd_entry,   1, 2, 4, 5,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd_browse,   2, 3, 3, 4,
+	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd_browse,  2, 3, 4, 5,
 					 GTK_FILL | GTK_EXPAND, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->play_sound,       0, 1, 4, 5,
+	gtk_table_attach(GTK_TABLE(table), dialog->play_sound,       0, 1, 5, 6,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_entry, 1, 2, 4, 5,
+	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_entry, 1, 2, 5, 6,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_browse, 2, 3, 4, 5,
+	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_browse,2, 3, 5, 6,
 					 GTK_FILL | GTK_EXPAND, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_test, 3, 4, 4, 5,
+	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_test,  3, 4, 5, 6,
 					 GTK_FILL | GTK_EXPAND, 0, 0, 0);
 
 	gtk_table_set_row_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE / 2);
@@ -714,7 +728,7 @@
 	gtk_widget_show(dialog->popup);
 	gtk_widget_show(dialog->popup_entry);
 	gtk_widget_show(dialog->send_msg);
-	gtk_widget_show(dialog->send_msg_entry);
+	gtk_widget_show(send_msg_imhtml);
 	gtk_widget_show(dialog->exec_cmd);
 	gtk_widget_show(dialog->exec_cmd_entry);
 	gtk_widget_show(dialog->exec_cmd_browse);
@@ -729,7 +743,7 @@
 
 	g_signal_connect(G_OBJECT(dialog->send_msg), "clicked",
 					 G_CALLBACK(pidgin_toggle_sensitive),
-					 dialog->send_msg_entry);
+					 send_msg_imhtml);
 
 	g_signal_connect(G_OBJECT(dialog->popup), "clicked",
 					 G_CALLBACK(pidgin_toggle_sensitive),
@@ -765,7 +779,12 @@
 	g_object_set_data_full(G_OBJECT(dialog->window), "sound-widgets",
 				sound_widgets, (GDestroyNotify)g_ptr_array_free);
 
-	g_signal_connect(G_OBJECT(dialog->send_msg_entry), "activate",
+	g_signal_connect_swapped(G_OBJECT(dialog->send_msg_entry), "format_function_clear",
+			G_CALLBACK(reset_send_msg_entry), dialog);
+	g_signal_connect_swapped(G_OBJECT(dialog->account_menu), "changed",
+			G_CALLBACK(reset_send_msg_entry), dialog);
+
+	g_signal_connect(G_OBJECT(dialog->send_msg_entry), "message_send",
 					 G_CALLBACK(save_pounce_cb), dialog);
 	g_signal_connect(G_OBJECT(dialog->popup_entry), "activate",
 					 G_CALLBACK(save_pounce_cb), dialog);
@@ -892,7 +911,7 @@
 													  "send-message",
 													  "message")) != NULL)
 		{
-			gtk_entry_set_text(GTK_ENTRY(dialog->send_msg_entry), value);
+			gtk_imhtml_append_text(GTK_IMHTML(dialog->send_msg_entry), value, 0);
 		}
 
 		if ((value = purple_pounce_action_get_attribute(cur_pounce,
--- a/pidgin/gtkprefs.c	Fri May 25 20:06:37 2007 +0000
+++ b/pidgin/gtkprefs.c	Fri May 25 22:06:53 2007 +0000
@@ -942,17 +942,7 @@
 
 	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
 
-	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold"))
-		gtk_imhtml_toggle_bold(GTK_IMHTML(imhtml));
-	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic"))
-		gtk_imhtml_toggle_italic(GTK_IMHTML(imhtml));
-	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline"))
-		gtk_imhtml_toggle_underline(GTK_IMHTML(imhtml));
-
-	gtk_imhtml_font_set_size(GTK_IMHTML(imhtml), purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size"));
-	gtk_imhtml_toggle_forecolor(GTK_IMHTML(imhtml), purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"));
-	gtk_imhtml_toggle_background(GTK_IMHTML(imhtml), purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"));
-	gtk_imhtml_toggle_fontface(GTK_IMHTML(imhtml), purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face"));
+	gtk_imhtml_setup_entry(GTK_IMHTML(imhtml), PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO);
 
 	g_signal_connect_after(G_OBJECT(imhtml), "format_function_toggle",
 					 G_CALLBACK(formatting_toggle_cb), toolbar);
--- a/pidgin/gtkutils.c	Fri May 25 20:06:37 2007 +0000
+++ b/pidgin/gtkutils.c	Fri May 25 22:06:53 2007 +0000
@@ -242,13 +242,14 @@
 		gtk_widget_show(to_toggle);
 }
 
-void pidgin_separator(GtkWidget *menu)
+GtkWidget *pidgin_separator(GtkWidget *menu)
 {
 	GtkWidget *menuitem;
 
 	menuitem = gtk_separator_menu_item_new();
 	gtk_widget_show(menuitem);
 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+	return menuitem;
 }
 
 GtkWidget *pidgin_new_item(GtkWidget *menu, const char *str)
@@ -499,6 +500,39 @@
 	return item;
 }
 
+static GdkPixbuf *
+pidgin_create_prpl_icon_from_prpl(PurplePlugin *prpl, PidginPrplIconSize size, PurpleAccount *account)
+{
+	PurplePluginProtocolInfo *prpl_info;
+	const char *protoname = NULL;
+	char buf[MAXPATHLEN];
+	char *filename = NULL;
+	GdkPixbuf *pixbuf;
+
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+	if (prpl_info->list_icon == NULL)
+		return NULL;
+
+	protoname = prpl_info->list_icon(account, NULL);
+	if (protoname == NULL)
+		return NULL;
+
+	/*
+	 * Status icons will be themeable too, and then it will look up
+	 * protoname from the theme
+	 */
+	g_snprintf(buf, sizeof(buf), "%s.png", protoname);
+
+	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols",
+				    size == PIDGIN_PRPL_ICON_SMALL ? "16" :
+				    size == PIDGIN_PRPL_ICON_MEDIUM ? "22" : "48",
+				    buf, NULL);
+	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
+	g_free(filename);
+
+	return pixbuf;
+}
+
 static GtkWidget *
 aop_option_menu_new(AopMenu *aop_menu, GCallback cb, gpointer user_data)
 {
@@ -549,25 +583,6 @@
 	}
 }
 
-static GdkPixbuf *
-get_prpl_pixbuf(PurplePluginProtocolInfo *prpl_info)
-{
-	const char *proto_name;
-	GdkPixbuf *pixbuf = NULL;
-	char *filename;
-	char buf[256];
-
-	proto_name = prpl_info->list_icon(NULL, NULL);
-	g_return_val_if_fail(proto_name != NULL, NULL);
-
-	g_snprintf(buf, sizeof(buf), "%s.png", proto_name);
-	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "16", buf, NULL);
-	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
-	g_free(filename);
-
-	return pixbuf;
-}
-
 static AopMenu *
 create_protocols_menu(const char *default_proto_id)
 {
@@ -611,7 +626,7 @@
 			gtalk_name = NULL;
 		}
 
-		pixbuf = get_prpl_pixbuf(prpl_info);
+		pixbuf = pidgin_create_prpl_icon_from_prpl(plugin, PIDGIN_PRPL_ICON_SMALL, NULL);
 
 		gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
 			aop_menu_item_new(sg, pixbuf, plugin->info->name, plugin->info->id));
@@ -666,7 +681,6 @@
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
 	for (p = list, i = 0; p != NULL; p = p->next, i++) {
-		PurplePluginProtocolInfo *prpl_info = NULL;
 		PurplePlugin *plugin;
 
 		if (show_all)
@@ -684,18 +698,12 @@
 
 		plugin = purple_find_prpl(purple_account_get_protocol_id(account));
 
-		if (plugin)
-			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
-
-		/* Load the image. */
-		if (prpl_info) {
-			pixbuf = get_prpl_pixbuf(prpl_info);
-
-			if (pixbuf) {
-				if (purple_account_is_disconnected(account) && show_all &&
-						purple_connections_get_all())
-					gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
-			}
+		pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+
+		if (pixbuf) {
+			if (purple_account_is_disconnected(account) && show_all &&
+					purple_connections_get_all())
+				gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
 		}
 
 		if (purple_account_get_alias(account)) {
@@ -879,6 +887,15 @@
 	g_free(filename);
 }
 
+void pidgin_retrieve_user_info(PurpleConnection *conn, const char *name)
+{
+	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
+	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
+	purple_notify_userinfo(conn, name, info, NULL, NULL);
+	purple_notify_user_info_destroy(info);
+	serv_get_info(conn, name);
+}
+
 gboolean
 pidgin_parse_x_im_contact(const char *msg, gboolean all_accounts,
 							PurpleAccount **ret_account, char **ret_protocol,
@@ -1580,40 +1597,13 @@
 pidgin_create_prpl_icon(PurpleAccount *account, PidginPrplIconSize size)
 {
 	PurplePlugin *prpl;
-	PurplePluginProtocolInfo *prpl_info;
-	const char *protoname = NULL;
-	char buf[256]; /* TODO: We should use a define for max file length */
-	char *filename = NULL;
-	GdkPixbuf *pixbuf;
 
 	g_return_val_if_fail(account != NULL, NULL);
 
 	prpl = purple_find_prpl(purple_account_get_protocol_id(account));
 	if (prpl == NULL)
 		return NULL;
-
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-	if (prpl_info->list_icon == NULL)
-		return NULL;
-
-	protoname = prpl_info->list_icon(account, NULL);
-	if (protoname == NULL)
-		return NULL;
-
-	/*
-	 * Status icons will be themeable too, and then it will look up
-	 * protoname from the theme
-	 */
-	g_snprintf(buf, sizeof(buf), "%s.png", protoname);
-
-	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols",
-				    size == PIDGIN_PRPL_ICON_SMALL ? "16" :
-				    size == PIDGIN_PRPL_ICON_MEDIUM ? "22" : "48",
-				    buf, NULL);
-	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
-	g_free(filename);
-
-	return pixbuf;
+	return pidgin_create_prpl_icon_from_prpl(prpl, size, account);
 }
 
 static void
@@ -1629,62 +1619,63 @@
 		callback(object, data);
 }
 
-void
+GtkWidget *
 pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
                             gpointer object)
 {
+	GtkWidget *menuitem;
+
 	if (act == NULL) {
-		pidgin_separator(menu);
-	} else {
-		GtkWidget *menuitem;
-
-		if (act->children == NULL) {
-			menuitem = gtk_menu_item_new_with_mnemonic(act->label);
-
-			if (act->callback != NULL) {
-				g_object_set_data(G_OBJECT(menuitem),
-				                  "purplecallback",
-				                  act->callback);
-				g_object_set_data(G_OBJECT(menuitem),
-				                  "purplecallbackdata",
-				                  act->data);
-				g_signal_connect(G_OBJECT(menuitem), "activate",
-				                 G_CALLBACK(menu_action_cb),
-				                 object);
-			} else {
-				gtk_widget_set_sensitive(menuitem, FALSE);
-			}
-
-			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+		return pidgin_separator(menu);
+	}
+
+	if (act->children == NULL) {
+		menuitem = gtk_menu_item_new_with_mnemonic(act->label);
+
+		if (act->callback != NULL) {
+			g_object_set_data(G_OBJECT(menuitem),
+							  "purplecallback",
+							  act->callback);
+			g_object_set_data(G_OBJECT(menuitem),
+							  "purplecallbackdata",
+							  act->data);
+			g_signal_connect(G_OBJECT(menuitem), "activate",
+							 G_CALLBACK(menu_action_cb),
+							 object);
 		} else {
-			GList *l = NULL;
-			GtkWidget *submenu = NULL;
-			GtkAccelGroup *group;
-
-			menuitem = gtk_menu_item_new_with_mnemonic(act->label);
-			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-
-			submenu = gtk_menu_new();
-			gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
-
-			group = gtk_menu_get_accel_group(GTK_MENU(menu));
-			if (group) {
-				char *path = g_strdup_printf("%s/%s", GTK_MENU_ITEM(menuitem)->accel_path, act->label);
-				gtk_menu_set_accel_path(GTK_MENU(submenu), path);
-				g_free(path);
-				gtk_menu_set_accel_group(GTK_MENU(submenu), group);
-			}
-
-			for (l = act->children; l; l = l->next) {
-				PurpleMenuAction *act = (PurpleMenuAction *)l->data;
-
-				pidgin_append_menu_action(submenu, act, object);
-			}
-			g_list_free(act->children);
-			act->children = NULL;
+			gtk_widget_set_sensitive(menuitem, FALSE);
 		}
-		purple_menu_action_free(act);
+
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+	} else {
+		GList *l = NULL;
+		GtkWidget *submenu = NULL;
+		GtkAccelGroup *group;
+
+		menuitem = gtk_menu_item_new_with_mnemonic(act->label);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+		submenu = gtk_menu_new();
+		gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+
+		group = gtk_menu_get_accel_group(GTK_MENU(menu));
+		if (group) {
+			char *path = g_strdup_printf("%s/%s", GTK_MENU_ITEM(menuitem)->accel_path, act->label);
+			gtk_menu_set_accel_path(GTK_MENU(submenu), path);
+			g_free(path);
+			gtk_menu_set_accel_group(GTK_MENU(submenu), group);
+		}
+
+		for (l = act->children; l; l = l->next) {
+			PurpleMenuAction *act = (PurpleMenuAction *)l->data;
+
+			pidgin_append_menu_action(submenu, act, object);
+		}
+		g_list_free(act->children);
+		act->children = NULL;
 	}
+	purple_menu_action_free(act);
+	return menuitem;
 }
 
 #if GTK_CHECK_VERSION(2,3,0)
--- a/pidgin/gtkutils.h	Fri May 25 20:06:37 2007 +0000
+++ b/pidgin/gtkutils.h	Fri May 25 22:06:53 2007 +0000
@@ -130,8 +130,10 @@
  * Adds a separator to a menu.
  *
  * @param menu The menu to add a separator to.
+ *
+ * @return The separator.
  */
-void pidgin_separator(GtkWidget *menu);
+GtkWidget *pidgin_separator(GtkWidget *menu);
 
 /**
  * Creates a menu item.
@@ -307,6 +309,14 @@
 void pidgin_load_accels(void);
 
 /**
+ * Get information about a user. Show immediate feedback.
+ *
+ * @param conn   The connection to get information from.
+ * @param name   The user to get information about.
+ */
+void pidgin_retrieve_user_info(PurpleConnection *conn, const char *name);
+
+/**
  * Parses an application/x-im-contact MIME message and returns the
  * data inside.
  *
@@ -404,8 +414,10 @@
  * @param menu    The menu to append to.
  * @param act     The PurpleMenuAction to append.
  * @param gobject The object to be passed to the action callback.
+ *
+ * @return   The menuitem added.
  */
-void pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
+GtkWidget *pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
                                  gpointer gobject);
 
 /**
--- a/pidgin/plugins/cap/cap.c	Fri May 25 20:06:37 2007 +0000
+++ b/pidgin/plugins/cap/cap.c	Fri May 25 22:06:53 2007 +0000
@@ -918,7 +918,8 @@
 
 static PidginPluginUiInfo ui_info = {
 	get_config_frame,
-	0 /* page_num (reserved) */
+	0 /* page_num (reserved) */,
+	NULL,NULL,NULL,NULL
 };
 
 static PurplePluginInfo info = {
@@ -944,7 +945,8 @@
 	&ui_info,										/**< ui_info		*/
 	NULL,											/**< extra_info	 */
 	NULL,											/**< prefs_info		*/
-	NULL
+	NULL,
+	NULL,NULL,NULL,NULL
 };
 
 static GtkWidget * get_config_frame(PurplePlugin *plugin) {