changeset 9030:7ab20f829190

[gaim-migrate @ 9806] Siege updated the code for creating right-click menu's for buddies, chats, groups, etc. It uses more stuff from the blist API and less stuff from multi.h. It also combines the code for right-click menus for chats, buddies, etc. (all types of blist nodes). So PRPLs and plugins can easily add right-click menu options to anything in the buddy list in a clean way. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Sun, 23 May 2004 17:27:45 +0000
parents 697e169dac12
children f2084fa7bda1
files src/blist.c src/blist.h src/gtkblist.c src/multi.h src/protocols/gg/gg.c src/protocols/irc/irc.c src/protocols/irc/irc.h src/protocols/jabber/buddy.c src/protocols/jabber/buddy.h src/protocols/jabber/jabber.c src/protocols/jabber/si.c src/protocols/jabber/si.h src/protocols/msn/msn.c src/protocols/napster/napster.c src/protocols/novell/novell.c src/protocols/oscar/oscar.c src/protocols/silc/buddy.c src/protocols/silc/chat.c src/protocols/silc/silc.c src/protocols/toc/toc.c src/protocols/trepia/trepia.c src/protocols/yahoo/yahoo.c src/protocols/zephyr/zephyr.c src/prpl.h src/roomlist.h
diffstat 25 files changed, 969 insertions(+), 764 deletions(-) [+]
line wrap: on
line diff
--- a/src/blist.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/blist.c	Sun May 23 17:27:45 2004 +0000
@@ -2720,25 +2720,28 @@
 /* XXX: end compat crap */
 
 
-GList *gaim_buddy_get_extended_menu(GaimBuddy *b) {
+GList *gaim_blist_node_get_extended_menu(GaimBlistNode *n) {
 	GList *menu = NULL;
-	gaim_signal_emit(gaim_blist_get_handle(), "buddy-extended-menu",
-		b, &menu);
+
+	g_return_val_if_fail(n, NULL);
+
+	gaim_signal_emit(gaim_blist_get_handle(),
+			"blist-node-extended-menu",
+			n, &menu);
 	return menu;
 }
 
-GList *gaim_chat_get_extended_menu(GaimChat *c) {
-	GList *menu = NULL;
-	gaim_signal_emit(gaim_blist_get_handle(), "chat-extended-menu",
-		c, &menu);
-	return menu;
-}
-
-GList *gaim_group_get_extended_menu(GaimGroup *g) {
-	GList *menu = NULL;
-	gaim_signal_emit(gaim_blist_get_handle(), "group-extended-menu",
-		g, &menu);
-	return menu;
+
+GaimBlistNodeAction *
+gaim_blist_node_action_new(char *label,
+			   void (*callback)(GaimBlistNode *, gpointer),
+			   gpointer data)
+{
+	GaimBlistNodeAction *act = g_new0(GaimBlistNodeAction, 1);
+	act->label = label;
+	act->callback = callback;
+	act->data = data;
+	return act;
 }
 
 
@@ -2812,20 +2815,11 @@
 										GAIM_SUBTYPE_BLIST_BUDDY));
 
 	gaim_signal_register(handle, "update-idle", gaim_marshal_VOID, NULL, 0);
-	gaim_signal_register(handle, "buddy-extended-menu",
+
+	gaim_signal_register(handle, "blist-node-extended-menu",
 			     gaim_marshal_VOID__POINTER_POINTER, NULL, 2,
 			     gaim_value_new(GAIM_TYPE_SUBTYPE,
-					    GAIM_SUBTYPE_BLIST_BUDDY),
-			     gaim_value_new(GAIM_TYPE_BOXED, "GList **"));
-	gaim_signal_register(handle, "chat-extended-menu",
-			     gaim_marshal_VOID__POINTER_POINTER, NULL, 2,
-			     gaim_value_new(GAIM_TYPE_SUBTYPE,
-					    GAIM_SUBTYPE_BLIST_CHAT),
-			     gaim_value_new(GAIM_TYPE_BOXED, "GList **"));
-	gaim_signal_register(handle, "group-extended-menu",
-			     gaim_marshal_VOID__POINTER_POINTER, NULL, 2,
-			     gaim_value_new(GAIM_TYPE_SUBTYPE,
-					    GAIM_SUBTYPE_BLIST_GROUP),
+					    GAIM_SUBTYPE_BLIST_NODE),
 			     gaim_value_new(GAIM_TYPE_BOXED, "GList **"));
 }
 
@@ -2834,3 +2828,4 @@
 {
 	gaim_signals_unregister_by_instance(gaim_blist_get_handle());
 }
+
--- a/src/blist.h	Sun May 23 08:06:38 2004 +0000
+++ b/src/blist.h	Sun May 23 17:27:45 2004 +0000
@@ -34,6 +34,8 @@
 typedef struct _GaimBlistUiOps GaimBlistUiOps;
 typedef struct _GaimBlistNode GaimBlistNode;
 
+typedef struct _GaimBlistNodeAction GaimBlistNodeAction;
+
 typedef struct _GaimChat GaimChat;
 typedef struct _GaimGroup GaimGroup;
 typedef struct _GaimContact GaimContact;
@@ -183,6 +185,14 @@
 	void (*request_add_group)(void);
 };
 
+
+struct _GaimBlistNodeAction {
+	char *label;
+	void (*callback)(GaimBlistNode *, gpointer);
+	gpointer data;
+};
+
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -857,22 +867,23 @@
 
 
 /**
- * Retrieves the extended menu items for a buddy.
- * @param b      The buddy to obtain the extended menu items for
-*/
-GList *gaim_buddy_get_extended_menu(GaimBuddy *b);
+ * Retrieves the extended menu items for a buddy list node.
+ * @param n	The blist node for which to obtain the extended menu items.
+ * @return	list of GaimBlistNodeAction items, as harvested by the
+ *		blist-node-extended-menu signal.
+ */
+GList *gaim_blist_node_get_extended_menu(GaimBlistNode *n);
+
 
 /**
- * Retrieves the extended menu items for a chat.
- * @param c      The chat to obtain the extended menu items for
-*/
-GList *gaim_chat_get_extended_menu(GaimChat *c);
-
-/**
- * Retrieves the extended menu items for a group.
- * @param g      The group to obtain the extended menu items for
-*/
-GList *gaim_group_get_extended_menu(GaimGroup *g);
+ * Creates a new GaimBlistNodeAction.
+ * @param label		The text label to display for this action.
+ * @param callback	The function to be called when the action is used on
+ *			a selected GaimBlistNode.
+ * @param data		Additional data, to be passed to the callback
+ */
+GaimBlistNodeAction  *gaim_blist_node_action_new(char *label,
+		void (*callback)(GaimBlistNode *, gpointer), gpointer data);
 
 
 /**************************************************************************/
--- a/src/gtkblist.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/gtkblist.c	Sun May 23 17:27:45 2004 +0000
@@ -1059,19 +1059,75 @@
 	}
 }
 
-static void gaim_proto_menu_cb(GtkMenuItem *item, GaimBuddy *b)
+
+static void
+blist_node_menu_cb(GtkMenuItem *item, GaimBlistNode *node)
+{
+	GaimBlistNodeAction *act;
+	act = (GaimBlistNodeAction *) g_object_get_data(G_OBJECT(item),
+			"gaimcallback");
+	if(act->callback)
+		act->callback(node, act->data);
+}
+
+
+static void
+append_blist_node_action (GtkWidget *menu, GaimBlistNodeAction *act,
+		GaimBlistNode *node, gboolean *dup_separator)
 {
-	struct proto_buddy_menu *pbm = g_object_get_data(G_OBJECT(item), "gaimcallback");
-	if (pbm->callback)
-		pbm->callback(pbm->gc, b->name);
+	if(act == NULL) {
+		if(! *dup_separator) {
+			gaim_separator(menu);
+			*dup_separator = TRUE;
+		}
+	} else {
+		GtkWidget *menuitem;
+
+		*dup_separator = FALSE;
+
+		menuitem = gtk_menu_item_new_with_mnemonic(act->label);
+		g_object_set_data(G_OBJECT(menuitem), "gaimcallback", act);
+		g_signal_connect(G_OBJECT(menuitem), "activate",
+				G_CALLBACK(blist_node_menu_cb), node);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+	}
 }
 
+
+static void
+append_blist_node_proto_menu (GtkWidget *menu, GaimConnection *gc, GaimBlistNode *node)
+{
+	GList *l, *ll;
+	gboolean dup_separator = FALSE;
+	GaimPluginProtocolInfo *prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+	if(!prpl_info || !prpl_info->blist_node_menu)
+		return;
+
+	for(l = ll = prpl_info->blist_node_menu(node); l; l = l->next) {
+		GaimBlistNodeAction *act = (GaimBlistNodeAction *) l->data;
+		append_blist_node_action(menu, act, node, &dup_separator);
+	}
+	g_list_free(ll);
+}
+
+
+static void
+append_blist_node_extended_menu (GtkWidget *menu, GaimBlistNode *node)
+{
+	GList *l, *ll;
+	gboolean dup_separator = FALSE;
+
+	for(l = ll = gaim_blist_node_get_extended_menu(node); l; l = l->next) {
+		GaimBlistNodeAction *act = (GaimBlistNodeAction *) l->data;
+		append_blist_node_action(menu, act, node, &dup_separator);
+	}
+	g_list_free(ll);
+}
+
+
 static void make_buddy_menu(GtkWidget *menu, GaimPluginProtocolInfo *prpl_info, GaimBuddy *b)
 {
-	GList *list = NULL, *l = NULL;
-	gboolean dup_separator = FALSE;
-	GtkWidget *menuitem;
-
 	if (prpl_info && prpl_info->get_info) {
 		gaim_new_item_from_stock(menu, _("Get _Info"), GAIM_STOCK_INFO,
 				G_CALLBACK(gtk_blist_menu_info_cb), b, 0, 0, NULL);
@@ -1083,65 +1139,14 @@
 	gaim_new_item_from_stock(menu, _("View _Log"), NULL,
 			G_CALLBACK(gtk_blist_menu_showlog_cb), b, 0, 0, NULL);
 
-	if (prpl_info && prpl_info->buddy_menu) {
-		list = prpl_info->buddy_menu(b->account->gc, b->name);
-
-		for(l = list; l; l = l->next) {
-			struct proto_buddy_menu *pbm = l->data;
-
-			/* draw NULL menu items as a separator.
-			   Also, do some simple checking to prevent
-			   doubled-up separators */
-			if(pbm == NULL) {
-				if(! dup_separator) {
-					gaim_separator(menu);
-					dup_separator = TRUE;
-				}
-				continue;
-			} else {
-				dup_separator = FALSE;
-			}
-
-			menuitem = gtk_menu_item_new_with_mnemonic(pbm->label);
-			g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm);
-			g_signal_connect(G_OBJECT(menuitem), "activate",
-				G_CALLBACK(gaim_proto_menu_cb), b);
-			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-		}
-		g_list_free(list);
-	}
-
-	/* check for additional menu items which may be added by other
-	   plugins. */
-	list = gaim_buddy_get_extended_menu(b);
-	for(l = list; l; l = l->next) {
-		struct proto_buddy_menu *pbm = l->data;
-
-		/* draw NULL menu items as a separator.  see previous,
-		   identical-looking code. */
-		if(pbm == NULL) {
-			if(! dup_separator) {
-				gaim_separator(menu);
-				dup_separator = TRUE;
-			}
-			continue;
-		} else {
-			dup_separator = FALSE;
-		}
-
-		menuitem = gtk_menu_item_new_with_mnemonic(pbm->label);
-		g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm);
-		g_signal_connect(G_OBJECT(menuitem), "activate",
-			G_CALLBACK(gaim_proto_menu_cb), b);
-		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-	}
-	g_list_free(list);
+	append_blist_node_proto_menu(menu, b->account->gc, (GaimBlistNode *) b);
+	append_blist_node_extended_menu(menu, (GaimBlistNode *) b);
 
 	/* moving on to the old ui-specific plugin menus */
-	gaim_signal_emit(gaim_gtk_blist_get_handle(),
-			"drawing-menu", menu, b);
+	gaim_signal_emit(gaim_gtk_blist_get_handle(), "drawing-menu", menu, b);
 
 	gaim_separator(menu);
+
 	gaim_new_item_from_stock(menu, _("_Alias..."), GAIM_STOCK_ALIAS,
 			G_CALLBACK(gtk_blist_menu_alias_cb), b, 0, 0, NULL);
 	gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE,
@@ -1183,20 +1188,11 @@
 	return FALSE;
 }
 
-static void gaim_proto_group_menu_cb(GtkMenuItem *item, GaimGroup *g)
-{
-	struct proto_group_menu *pgm = g_object_get_data(G_OBJECT(item), "gaimcallback");
-	if (pgm->callback)
-		pgm->callback(g);
-}
 
 static GtkWidget *
 create_group_menu (GaimBlistNode *node, GaimGroup *g)
 {
 	GtkWidget *menu;
-	GList *list = NULL, *l = NULL;
-	gboolean dup_separator = FALSE;
-	GtkWidget *menuitem;
 
 	menu = gtk_menu_new();
 	gaim_new_item_from_stock(menu, _("Add a _Buddy"), GTK_STOCK_ADD,
@@ -1208,39 +1204,11 @@
 	gaim_new_item_from_stock(menu, _("_Rename"), NULL,
 				 G_CALLBACK(show_rename_group), node, 0, 0, NULL);
 
-	list = gaim_group_get_extended_menu(g);
-	for(l = list; l; l = l->next) {
-		struct proto_group_menu *pgm = l->data;
-
-		/* draw NULL menu items as a separator.  see previous,
-		   identical-looking code. (in make_buddy_menu)*/
-		if(pgm == NULL) {
-			if(! dup_separator) {
-				gaim_separator(menu);
-				dup_separator = TRUE;
-			}
-			continue;
-		} else {
-			dup_separator = FALSE;
-		}
-
-		menuitem = gtk_menu_item_new_with_mnemonic(pgm->label);
-		g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pgm);
-		g_signal_connect(G_OBJECT(menuitem), "activate",
-			G_CALLBACK(gaim_proto_group_menu_cb), g);
-		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-	}
-	g_list_free(list);
+	append_blist_node_extended_menu(menu, node);
 
 	return menu;
 }
 
-static void gaim_proto_chat_menu_cb(GtkMenuItem *item, GaimChat *c)
-{
-	struct proto_chat_menu *pcm = g_object_get_data(G_OBJECT(item), "gaimcallback");
-	if (pcm->callback)
-		pcm->callback(pcm->gc, c->components);
-}
 
 static GtkWidget *
 create_chat_menu (GaimBlistNode *node,
@@ -1249,78 +1217,25 @@
 		  GaimPluginProtocolInfo *prpl_info)
 {
 	GtkWidget *menu;
-	GList *list, *l;
-	GtkWidget *menuitem;
-	gboolean dup_separator = FALSE;
-	gboolean autojoin = (gaim_blist_node_get_bool(node,
-						     "gtk-autojoin") || (gaim_blist_node_get_string(node,
-									 "gtk-autojoin") != NULL));
+	gboolean autojoin;
 
 	menu = gtk_menu_new();
+	autojoin = (gaim_blist_node_get_bool(node, "gtk-autojoin") ||
+			(gaim_blist_node_get_string(node, "gtk-autojoin") != NULL));
+
 	gaim_new_item_from_stock(menu, _("_Join"), GAIM_STOCK_CHAT,
-				 G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL);
+			G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL);
 	gaim_new_check_item(menu, _("Auto-Join"),
-			    G_CALLBACK(gtk_blist_menu_autojoin_cb), node,
-				autojoin);
-
-	if (prpl_info && prpl_info->chat_menu) {
-		list = prpl_info->chat_menu(c->account->gc, c->components);
-		while (list) {
-			struct proto_chat_menu *pcm = list->data;
-
-			/* draw NULL menu items as a separator.
-			   Also, do some simple checking to prevent
-			   doubled-up separators */
-			if(pcm == NULL) {
-				if(! dup_separator) {
-					gaim_separator(menu);
-					dup_separator = TRUE;
-				}
-				continue;
-			} else {
-				dup_separator = FALSE;
-			}
-
-			menuitem = gtk_menu_item_new_with_mnemonic(pcm->label);
-			g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pcm);
-			g_signal_connect(G_OBJECT(menuitem), "activate",
-					 G_CALLBACK(gaim_proto_chat_menu_cb), c);
-			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-			list = list->next;
-		}
-	}
-
-	/* check for additional menu items which may be added by other
-	   plugins. */
-	list = gaim_chat_get_extended_menu(c);
-	for(l = list; l; l = l->next) {
-		struct proto_chat_menu *pcm = l->data;
-
-		/* draw NULL menu items as a separator.  see previous,
-		   identical-looking code. */
-		if(pcm == NULL) {
-			if(! dup_separator) {
-				gaim_separator(menu);
-				dup_separator = TRUE;
-			}
-			continue;
-		} else {
-			dup_separator = FALSE;
-		}
-
-		menuitem = gtk_menu_item_new_with_mnemonic(pcm->label);
-		g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pcm);
-		g_signal_connect(G_OBJECT(menuitem), "activate",
-			G_CALLBACK(gaim_proto_chat_menu_cb), c);
-		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-	}
-	g_list_free(list);
+			G_CALLBACK(gtk_blist_menu_autojoin_cb), node, autojoin);
+
+	append_blist_node_proto_menu(menu, c->account->gc, node);
+	append_blist_node_extended_menu(menu, node);
 
 	/* moving on to the old ui-specific plugin menus */
-	gaim_signal_emit(gaim_gtk_blist_get_handle(),
-			"drawing-menu", menu, c);
+	gaim_signal_emit(gaim_gtk_blist_get_handle(), "drawing-menu", menu, c);
 
 	gaim_separator(menu);
+
 	gaim_new_item_from_stock(menu, _("_Alias..."), GAIM_STOCK_ALIAS,
 				 G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL);
 	gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE,
--- a/src/multi.h	Sun May 23 08:06:38 2004 +0000
+++ b/src/multi.h	Sun May 23 17:27:45 2004 +0000
@@ -24,25 +24,6 @@
 #ifndef _MULTI_H_
 #define _MULTI_H_
 
-#include "account.h"
-
-struct proto_buddy_menu {
-	char *label;
-	void (*callback)(GaimConnection *, const char *);
-	GaimConnection *gc;
-};
-
-struct proto_chat_menu {
-	char *label;
-	void (*callback)(GaimConnection *, GHashTable *);
-	GaimConnection *gc;
-};
-
-struct proto_group_menu {
-	char *label;
-	void (*callback)(GaimGroup *);
-};
-
 struct proto_chat_entry {
 	char *label;
 	char *identifier;
--- a/src/protocols/gg/gg.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/gg/gg.c	Sun May 23 17:27:45 2004 +0000
@@ -1,6 +1,6 @@
 /*
  * gaim - Gadu-Gadu Protocol Plugin
- * $Id: gg.c 9791 2004-05-22 17:33:38Z lschiere $
+ * $Id: gg.c 9806 2004-05-23 17:27:45Z thekingant $
  *
  * Copyright (C) 2001 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
  *
@@ -26,8 +26,8 @@
 
 #include "account.h"
 #include "accountopt.h"
+#include "blist.h"
 #include "debug.h"
-#include "multi.h"
 #include "notify.h"
 #include "proxy.h"
 #include "prpl.h"
@@ -244,27 +244,35 @@
 }
 
 /* Enhance these functions, more options and such stuff */
-static GList *agg_buddy_menu(GaimConnection *gc, const char *who)
+static GList *agg_buddy_menu(GaimBuddy *buddy)
 {
 	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
-	GaimBuddy *b = gaim_find_buddy(gc->account, who);
+	GaimBlistNodeAction *act;
+
 	static char buf[AGG_BUF_LEN];
+	g_snprintf(buf, sizeof(buf), _("Status: %s"), get_away_text(buddy->uc));
 
-	if (!b) {
-		return m;
-	}
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	g_snprintf(buf, sizeof(buf), _("Status: %s"), get_away_text(b->uc));
-	pbm->label = buf;
-	pbm->callback = NULL;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
+	/* um... this seems silly. since in this pass, I'm only converting
+	   over the menu building, I'm not going to mess with it though */
+	/* XXX: shouldn't this be in the tooltip instead? */
+	act = gaim_blist_node_action_new(buf, NULL, NULL);
+	m = g_list_append(m, act);
 
 	return m;
 }
 
+
+static GList *
+agg_blist_node_menu(GaimBlistNode *node)
+{
+	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		return agg_buddy_menu((GaimBuddy *) node);
+	} else {
+		return NULL;
+	}
+}
+
+
 static void agg_load_buddy_list(GaimConnection *gc, char *buddylist)
 {
 	struct agg_data *gd = (struct agg_data *)gc->proto_data;
@@ -1530,7 +1538,7 @@
 	NULL,
 	NULL,
 	agg_away_states,
-	agg_buddy_menu,
+	agg_blist_node_menu,
 	NULL,
 	agg_login,
 	agg_close,
@@ -1575,7 +1583,6 @@
 	NULL,
 	NULL,
 	NULL,
-	NULL
 };
 
 static GaimPluginInfo info =
@@ -1587,7 +1594,7 @@
 	NULL,                                             /**< dependencies   */
 	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
 
-	"prpl-gg",		                                  /**< id             */
+	"prpl-gg",		                          /**< id             */
 	"Gadu-Gadu",                                      /**< name           */
 	VERSION,                                          /**< version        */
 	                                                  /**  summary        */
@@ -1613,9 +1620,9 @@
 	GaimAccountOption *option;
 
 	option = gaim_account_option_string_new(_("Nick"), "nick",
-											"Gadu-Gadu User");
+			"Gadu-Gadu User");
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
+			option);
 
 	my_protocol = plugin;
 }
--- a/src/protocols/irc/irc.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/irc/irc.c	Sun May 23 17:27:45 2004 +0000
@@ -25,15 +25,16 @@
 
 #include "internal.h"
 
-#include "plugin.h"
 #include "accountopt.h"
-#include "multi.h"
-#include "prpl.h"
+#include "blist.h"
 #include "conversation.h"
+#include "debug.h"
+#include "multi.h"
 #include "notify.h"
-#include "debug.h"
-#include "blist.h"
+#include "prpl.h"
+#include "plugin.h"
 #include "util.h"
+
 #include "irc.h"
 
 static void irc_buddy_append(char *name, struct irc_buddy *ib, GString *string);
@@ -143,16 +144,31 @@
 	return list;
 }
 
-static GList *irc_buddy_menu(GaimConnection *gc, const char *who)
+
+static void irc_dccsend_send_ask_menu(GaimBlistNode *node, gpointer data)
+{
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc =gaim_account_get_connection(buddy->account);
+
+	irc_dccsend_send_ask(gc, buddy->name);
+}
+
+
+static GList *irc_blist_node_menu(GaimBlistNode *node)
 {
 	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
-	
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Send File");
-	pbm->callback = irc_dccsend_send_ask;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
+	GaimBlistNodeAction *act;
+
+	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		act = gaim_blist_node_action_new(_("Send File"),
+				irc_dccsend_send_ask_menu, NULL);
+		m = g_list_append(m, act);
+	}
 	
 	return m;
 }
@@ -545,7 +561,7 @@
 	NULL,
 	NULL,
 	irc_away_states,
-	irc_buddy_menu,
+	irc_blist_node_menu,
 	irc_chat_join_info,
 	irc_login,
 	irc_close,
@@ -589,7 +605,6 @@
 	NULL,
 	irc_roomlist_get_list,
 	irc_roomlist_cancel,
-	NULL,
 	NULL
 };
 
--- a/src/protocols/irc/irc.h	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/irc/irc.h	Sun May 23 17:27:45 2004 +0000
@@ -25,7 +25,6 @@
 
 #include <glib.h>
 
-#include "multi.h"
 #include "roomlist.h"
 
 #define IRC_DEFAULT_SERVER "irc.freenode.net"
--- a/src/protocols/jabber/buddy.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/jabber/buddy.c	Sun May 23 17:27:45 2004 +0000
@@ -835,93 +835,145 @@
 	xmlnode_free(presence);
 }
 
-static void jabber_buddy_make_invisible(GaimConnection *gc, const char *name)
+static void jabber_buddy_make_invisible(GaimBlistNode *node, gpointer data)
 {
-	JabberStream *js = gc->proto_data;
-	jabber_buddy_set_invisibility(js, name, TRUE);
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+	JabberStream *js;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+	js = gc->proto_data;
+
+	jabber_buddy_set_invisibility(js, buddy->name, TRUE);
 }
 
-static void jabber_buddy_make_visible(GaimConnection *gc, const char *name)
+static void jabber_buddy_make_visible(GaimBlistNode *node, gpointer data)
 {
-	JabberStream *js = gc->proto_data;
-	jabber_buddy_set_invisibility(js, name, FALSE);
-}
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+	JabberStream *js;
 
-static void jabber_buddy_cancel_presence_notification(GaimConnection *gc,
-		const char *name)
-{
-	JabberStream *js = gc->proto_data;
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
 
-	/* I wonder if we should prompt the user before doing this */
-	jabber_presence_subscription_set(js, name, "unsubscribed");
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+	js = gc->proto_data;
+
+	jabber_buddy_set_invisibility(js, buddy->name, FALSE);
 }
 
-static void jabber_buddy_rerequest_auth(GaimConnection *gc, const char *name)
+static void jabber_buddy_cancel_presence_notification(GaimBlistNode *node,
+		gpointer data)
 {
-	JabberStream *js = gc->proto_data;
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+	JabberStream *js;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
 
-	jabber_presence_subscription_set(js, name, "subscribe");
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+	js = gc->proto_data;
+
+	/* I wonder if we should prompt the user before doing this */
+	jabber_presence_subscription_set(js, buddy->name, "unsubscribed");
 }
 
-static void jabber_buddy_unsubscribe(GaimConnection *gc, const char *name)
+static void jabber_buddy_rerequest_auth(GaimBlistNode *node, gpointer data)
 {
-	JabberStream *js = gc->proto_data;
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+	JabberStream *js;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
 
-	jabber_presence_subscription_set(js, name, "unsubscribe");
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+	js = gc->proto_data;
+
+	jabber_presence_subscription_set(js, buddy->name, "subscribe");
 }
 
-GList *jabber_buddy_menu(GaimConnection *gc, const char *name)
+
+static void jabber_buddy_unsubscribe(GaimBlistNode *node, gpointer data)
 {
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+	JabberStream *js;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+	js = gc->proto_data;
+
+	jabber_presence_subscription_set(js, buddy->name, "unsubscribe");
+}
+
+
+GList *jabber_buddy_menu(GaimBuddy *buddy)
+{
+	GaimConnection *gc = gaim_account_get_connection(buddy->account);
+	JabberStream *js = gc->proto_data;
+	JabberBuddy *jb = jabber_buddy_find(js, buddy->name, TRUE);
+
 	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
-	JabberStream *js = gc->proto_data;
-	JabberBuddy *jb = jabber_buddy_find(js, name, TRUE);
+	GaimBlistNodeAction *act;
 
 	if(!jb)
 		return m;
 
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Send File");
-	pbm->callback = jabber_si_xfer_ask_send;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
+	act = gaim_blist_node_action_new(_("Send File"),
+			jabber_si_xfer_ask_send, NULL);
+	m = g_list_append(m, act);
 
 	/* XXX: fix the NOT ME below */
 
 	if(js->protocol_version == JABBER_PROTO_0_9 /* && NOT ME */) {
-		pbm = g_new0(struct proto_buddy_menu, 1);
 		if(jb->invisible & JABBER_INVIS_BUDDY) {
-			pbm->label = _("Un-hide From");
-			pbm->callback = jabber_buddy_make_visible;
+			act = gaim_blist_node_action_new(_("Un-hide From"),
+					jabber_buddy_make_visible, NULL);
 		} else {
-			pbm->label = _("Temporarily Hide From");
-			pbm->callback = jabber_buddy_make_invisible;
+			act = gaim_blist_node_action_new(_("Temporarily Hide From"),
+					jabber_buddy_make_invisible, NULL);
 		}
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		m = g_list_append(m, act);
 	}
 
 	if(jb->subscription & JABBER_SUB_FROM /* && NOT ME */) {
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("Cancel Presence Notification");
-		pbm->callback = jabber_buddy_cancel_presence_notification;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		act = gaim_blist_node_action_new(_("Cancel Presence Notification"),
+				jabber_buddy_cancel_presence_notification, NULL);
+		m = g_list_append(m, act);
 	}
 
 	if(!(jb->subscription & JABBER_SUB_TO)) {
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("(Re-)Request authorization");
-		pbm->callback = jabber_buddy_rerequest_auth;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		act = gaim_blist_node_action_new(_("(Re-)Request authorization"),
+				jabber_buddy_rerequest_auth, NULL);
+		m = g_list_append(m, act);
+
 	} else /* if(NOT ME) */{
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("Unsubscribe");
-		pbm->callback = jabber_buddy_unsubscribe;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+
+		/* shouldn't this just happen automatically when the buddy is
+		   removed? */
+		act = gaim_blist_node_action_new(_("Unsubscribe"),
+				jabber_buddy_unsubscribe, NULL);
+		m = g_list_append(m, act);
 	}
 
 	return m;
 }
+
+GList *
+jabber_blist_node_menu(GaimBlistNode *node)
+{
+	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		return jabber_buddy_menu((GaimBuddy *) node);
+	} else {
+		return NULL;
+	}
+}
+
+
--- a/src/protocols/jabber/buddy.h	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/jabber/buddy.h	Sun May 23 17:27:45 2004 +0000
@@ -66,7 +66,7 @@
 void jabber_buddy_get_info_chat(GaimConnection *gc, int id,
 		const char *resource);
 
-GList *jabber_buddy_menu(GaimConnection *gc, const char *name);
+GList *jabber_blist_node_menu(GaimBlistNode *node);
 
 void jabber_set_info(GaimConnection *gc, const char *info);
 void jabber_setup_set_info(GaimPluginAction *action);
--- a/src/protocols/jabber/jabber.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/jabber/jabber.c	Sun May 23 17:27:45 2004 +0000
@@ -22,9 +22,9 @@
 
 #include "account.h"
 #include "accountopt.h"
+#include "blist.h"
 #include "debug.h"
 #include "message.h"
-#include "multi.h"
 #include "notify.h"
 #include "pluginpref.h"
 #include "prpl.h"
@@ -1297,7 +1297,7 @@
 	jabber_status_text,
 	jabber_tooltip_text,
 	jabber_away_states,
-	jabber_buddy_menu,
+	jabber_blist_node_menu,
 	jabber_chat_info,
 	jabber_login,
 	jabber_close,
@@ -1341,7 +1341,6 @@
 	jabber_find_blist_chat,
 	jabber_roomlist_get_list,
 	jabber_roomlist_cancel,
-	NULL,
 	NULL
 };
 
--- a/src/protocols/jabber/si.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/jabber/si.c	Sun May 23 17:27:45 2004 +0000
@@ -18,6 +18,9 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  */
+
+#include "blist.h"
+
 #include "internal.h"
 #include "debug.h"
 #include "ft.h"
@@ -641,16 +644,25 @@
 	}
 }
 
-void jabber_si_xfer_ask_send(GaimConnection *gc, const char *name)
+void jabber_si_xfer_ask_send(GaimBlistNode *node, gpointer data)
 {
-	JabberStream *js = gc->proto_data;
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+	JabberStream *js;
+
 	GaimXfer *xfer;
 	JabberSIXfer *jsx;
 
-	if(!gaim_find_buddy(gc->account, name) || !jabber_buddy_find(js, name, FALSE))
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+	js = gc->proto_data;
+
+	if(!gaim_find_buddy(gc->account, buddy->name) || !jabber_buddy_find(js, buddy->name, FALSE))
 		return;
 
-	xfer = gaim_xfer_new(gaim_connection_get_account(gc), GAIM_XFER_SEND, name);
+	xfer = gaim_xfer_new(buddy->account, GAIM_XFER_SEND, buddy->name);
 
 	xfer->data = jsx = g_new0(JabberSIXfer, 1);
 	jsx->js = js;
--- a/src/protocols/jabber/si.h	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/jabber/si.h	Sun May 23 17:27:45 2004 +0000
@@ -28,6 +28,6 @@
 
 void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet);
 void jabber_si_parse(JabberStream *js, xmlnode *packet);
-void jabber_si_xfer_ask_send(GaimConnection *gc, const char *name);
+void jabber_si_xfer_ask_send(GaimBlistNode *node, gpointer data);
 
 #endif /* _GAIM_JABBER_SI_H_ */
--- a/src/protocols/msn/msn.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/msn/msn.c	Sun May 23 17:27:45 2004 +0000
@@ -20,7 +20,9 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #include <glib.h>
+#include "internal.h"
 
+#include "blist.h"
 #include "msn.h"
 #include "accountopt.h"
 #include "msg.h"
@@ -30,7 +32,6 @@
 #include "session.h"
 #include "state.h"
 #include "utils.h"
-#include "multi.h"
 #include "util.h"
 
 #include "notification.h"
@@ -234,53 +235,67 @@
 {
 	GaimConnection *gc = (GaimConnection *) action->context;
 	gaim_request_action(gc, NULL, _("Allow MSN Mobile pages?"),
-						_("Do you want to allow or disallow people on "
-						  "your buddy list to send you MSN Mobile pages "
-						  "to your cell phone or other mobile device?"),
-						-1, gc, 3,
-						_("Allow"), G_CALLBACK(enable_msn_pages_cb),
-						_("Disallow"), G_CALLBACK(disable_msn_pages_cb),
-						_("Cancel"), NULL);
+			_("Do you want to allow or disallow people on "
+			  "your buddy list to send you MSN Mobile pages "
+			  "to your cell phone or other mobile device?"),
+			-1, gc, 3,
+			_("Allow"), G_CALLBACK(enable_msn_pages_cb),
+			_("Disallow"), G_CALLBACK(disable_msn_pages_cb),
+			_("Cancel"), NULL);
 }
 
 static void
-show_send_to_mobile_cb(GaimConnection *gc, const char *passport)
+show_send_to_mobile_cb(GaimBlistNode *node, gpointer ignored)
 {
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+	
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+
 	MsnUser *user;
 	MsnSession *session;
 	MsnMobileData *data;
 
 	session = gc->proto_data;
-	user = msn_users_find_with_passport(session->users, passport);
+	user = msn_users_find_with_passport(session->users, buddy->name);
 
 	data = g_new0(MsnMobileData, 1);
 	data->gc = gc;
-	data->passport = passport;
+	data->passport = buddy->name;
 
 	gaim_request_input(gc, NULL, _("Send a mobile message."), NULL,
-					   NULL, TRUE, FALSE, NULL,
-					   _("Page"), G_CALLBACK(send_to_mobile_cb),
-					   _("Close"), G_CALLBACK(close_mobile_page_cb),
-					   data);
+			NULL, TRUE, FALSE, NULL,
+			_("Page"), G_CALLBACK(send_to_mobile_cb),
+			_("Close"), G_CALLBACK(close_mobile_page_cb),
+			data);
 }
 
 static void
-initiate_chat_cb(GaimConnection *gc, const char *passport)
+initiate_chat_cb(GaimBlistNode *node, gpointer data)
 {
-	GaimAccount *account;
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+
 	MsnSession *session;
 	MsnCmdProc *cmdproc;
 	MsnSwitchBoard *swboard;
 	MsnUser *user;
+	
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
 
-	account = gaim_connection_get_account(gc);
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+
 	session = gc->proto_data;
 	cmdproc = session->notification_conn->cmdproc;
 
 	if ((swboard = msn_session_open_switchboard(session)) == NULL)
 		return;
 
-	user = msn_user_new(session, passport, NULL);
+	user = msn_user_new(session, buddy->name, NULL);
 
 	msn_switchboard_set_user(swboard, user);
 
@@ -291,7 +306,7 @@
 	swboard->chat = serv_got_joined_chat(gc, swboard->chat_id, "MSN Chat");
 
 	gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->chat),
-							gaim_account_get_username(account), NULL);
+			gaim_account_get_username(buddy->account), NULL);
 }
 
 /**************************************************************************
@@ -410,45 +425,46 @@
 }
 
 static GList *
-msn_buddy_menu(GaimConnection *gc, const char *who)
+msn_buddy_menu(GaimBuddy *buddy)
 {
-	GaimAccount *account;
 	MsnUser *user;
-	struct proto_buddy_menu *pbm;
-	GaimBuddy *b;
-	GList *m = NULL;
 
-	account = gaim_connection_get_account(gc);
-	b = gaim_find_buddy(account, who);
+	GList *m = NULL;
+	GaimBlistNodeAction *act;
 
-	g_return_val_if_fail(b != NULL, NULL);
-
-	user = b->proto_data;
-
+	user = buddy->proto_data;
 	if (user != NULL)
 	{
 		if (user->mobile)
 		{
-			pbm = g_new0(struct proto_buddy_menu, 1);
-			pbm->label    = _("Send to Mobile");
-			pbm->callback = show_send_to_mobile_cb;
-			pbm->gc       = gc;
-			m = g_list_append(m, pbm);
+			act = gaim_blist_node_action_new(_("Send to Mobile"),
+					show_send_to_mobile_cb, NULL);
+			m = g_list_append(m, act);
 		}
 	}
 
-	if (g_ascii_strcasecmp(who, gaim_account_get_username(account)))
+	if (g_ascii_strcasecmp(buddy->name, gaim_account_get_username(buddy->account)))
 	{
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label    = _("Initiate Chat");
-		pbm->callback = initiate_chat_cb;
-		pbm->gc       = gc;
-		m = g_list_append(m, pbm);
+		act = gaim_blist_node_action_new(_("Initiate Chat"),
+				initiate_chat_cb, NULL);
+		m = g_list_append(m, act);
 	}
 
 	return m;
 }
 
+
+static GList *
+msn_blist_node_menu(GaimBlistNode *node)
+{
+	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		return msn_buddy_menu((GaimBuddy *) node);
+	} else {
+		return NULL;
+	}
+}
+
+
 static void
 msn_login(GaimAccount *account)
 {
@@ -1626,7 +1642,7 @@
 	msn_status_text,
 	msn_tooltip_text,
 	msn_away_states,
-	msn_buddy_menu,
+	msn_blist_node_menu,
 	NULL,
 	msn_login,
 	msn_close,
@@ -1670,7 +1686,6 @@
 	NULL,
 	NULL,
 	NULL,
-	NULL,
 	NULL
 };
 
--- a/src/protocols/napster/napster.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/napster/napster.c	Sun May 23 17:27:45 2004 +0000
@@ -21,6 +21,7 @@
 
 #include "account.h"
 #include "accountopt.h"
+#include "blist.h"
 #include "conversation.h"
 #include "debug.h"
 #include "multi.h"
@@ -532,20 +533,6 @@
 		*se = "offline";
 }
 
-static GList *nap_buddy_menu(GaimConnection *gc, const char *who)
-{
-	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Get Info");
-	pbm->callback = nap_get_info;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-
-	return m;
-}
-
 static GList *nap_chat_info(GaimConnection *gc)
 {
 	GList *m = NULL;
@@ -572,7 +559,7 @@
 	NULL,
 	NULL,
 	NULL,
-	nap_buddy_menu,
+	NULL,
 	nap_chat_info,
 	nap_login,
 	nap_close,
@@ -616,7 +603,6 @@
 	NULL,
 	NULL,
 	NULL,
-	NULL,
 	NULL
 };
 
--- a/src/protocols/novell/novell.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/novell/novell.c	Sun May 23 17:27:45 2004 +0000
@@ -1519,20 +1519,28 @@
 }
 
 static void
-_initiate_conference_cb(GaimConnection *gc, const char *who)
+_initiate_conference_cb(GaimBlistNode *node, gpointer ignored)
 {
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+
 	NMUser *user;
 	const char *conf_name;
 	GaimConversation *chat = NULL;
 	NMUserRecord *user_record;
 	NMConference *conference;
 
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+
 	user = gc->proto_data;
 	if (user == NULL)
 		return;
 
 	/* We should already have a userrecord for the buddy */
-	user_record = nm_find_user_record(user, who);
+	user_record = nm_find_user_record(user, buddy->name);
 	if (user_record == NULL)
 		return;
 
@@ -3274,16 +3282,16 @@
 }
 
 static GList *
-novell_buddy_menu(GaimConnection *gc, const char *who)
+novell_blist_node_menu(GaimBlistNode *node)
 {
 	GList *list = NULL;
-	struct proto_buddy_menu *pbm;
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Initiate _Chat");
-	pbm->callback = _initiate_conference_cb;
-	pbm->gc = gc;
-	list = g_list_append(list, pbm);
+	GaimBlistNodeAction *act;	
+
+	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		act = gaim_blist_node_action_new(_("Initiate _Chat"),
+				_initiate_conference_cb, NULL);
+		list = g_list_append(list, act);
+	}
 
 	return list;
 }
@@ -3298,7 +3306,7 @@
 	novell_status_text,
 	novell_tooltip_text,
 	novell_away_states,
-	novell_buddy_menu,
+	novell_blist_node_menu,
 	NULL,						/* chat_info */
 	novell_login,
 	novell_close,
@@ -3337,6 +3345,11 @@
 	NULL,						/* normalize */
 	NULL,						/* set_buddy_icon */
 	novell_remove_group,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
 	NULL
 };
 
--- a/src/protocols/oscar/oscar.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/oscar/oscar.c	Sun May 23 17:27:45 2004 +0000
@@ -1344,20 +1344,31 @@
 }
 
 /* this is the right click menu cb thingy */
-static void oscar_ask_direct_im(GaimConnection *gc, const char *who) {
+static void oscar_ask_direct_im(GaimBlistNode *node, gpointer ignored) {
+
+	GaimBuddy *buddy;
+	GaimConnection *gc;
 	gchar *buf;
-	struct ask_do_dir_im *data = g_new0(struct ask_do_dir_im, 1);
-	data->who = g_strdup(who);
+	struct ask_do_dir_im *data;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+
+	data = g_new0(struct ask_do_dir_im, 1);
+	data->who = g_strdup(buddy->name);
 	data->gc = gc;
-	buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."), who);
+	buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."),
+			buddy->name);
 
 	gaim_request_action(gc, NULL, buf,
-						_("Because this reveals your IP address, it "
-						  "may be considered a privacy risk.  Do you "
-						  "wish to continue?"),
-						0, data, 2,
-						_("Connect"), G_CALLBACK(oscar_direct_im),
-						_("Cancel"), G_CALLBACK(oscar_cancel_direct_im));
+			_("Because this reveals your IP address, it "
+			  "may be considered a privacy risk.  Do you "
+			  "wish to continue?"),
+			0, data, 2,
+			_("Connect"), G_CALLBACK(oscar_direct_im),
+			_("Cancel"), G_CALLBACK(oscar_cancel_direct_im));
 	g_free(buf);
 }
 
@@ -1908,20 +1919,30 @@
 	}
 }
 
-static void oscar_ask_sendfile(GaimConnection *gc, const char *destsn) {
-	OscarData *od = (OscarData *)gc->proto_data;
+static void oscar_ask_sendfile(GaimBlistNode *node, gpointer data) {
+
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+
+	OscarData *od;
 	GaimXfer *xfer;
 	struct aim_oft_info *oft_info;
 	const char *ip;
 
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+	od = (OscarData *)gc->proto_data;
+
 	/* You want to send a file to someone else, you're so generous */
 
 	/* Build the file transfer handle */
-	xfer = gaim_xfer_new(gaim_connection_get_account(gc), GAIM_XFER_SEND, destsn);
+	xfer = gaim_xfer_new(buddy->account, GAIM_XFER_SEND, buddy->name);
 
 	/* Create the oscar-specific data */
 	ip = gaim_network_get_my_ip(od->conn ? od->conn->fd : -1);
-	oft_info = aim_oft_createinfo(od->sess, NULL, destsn, ip, 0, 0, 0, NULL);
+	oft_info = aim_oft_createinfo(od->sess, NULL, buddy->name, ip, 0, 0, 0, NULL);
 	xfer->data = oft_info;
 
 	 /* Setup our I/O op functions */
@@ -3324,7 +3345,8 @@
 	oscar_free_name_data(data);
 }
 
-static void gaim_auth_sendrequest(GaimConnection *gc, const char *name) {
+
+static void gaim_auth_sendrequest(GaimConnection *gc, char *name) {
 	struct name_data *data = g_new(struct name_data, 1);
 	GaimBuddy *buddy;
 	gchar *dialog_msg, *nombre;
@@ -3350,6 +3372,18 @@
 	g_free(nombre);
 }
 
+
+static void gaim_auth_sendrequest_menu(GaimBlistNode *node, gpointer ignored) {
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+	gaim_auth_sendrequest(gc, buddy->name);
+}
+
 /* When other people ask you for authorization */
 static void gaim_auth_grant(struct name_data *data) {
 	GaimConnection *gc = data->gc;
@@ -5068,7 +5102,7 @@
 	}
 	return 0;
 }
-static void oscar_ask_direct_im(GaimConnection *gc, const char *name);
+
 static int gaim_odc_send_im(aim_session_t *, aim_conn_t *, const char *, GaimConvImFlags);
 
 static int oscar_send_im(GaimConnection *gc, const char *name, const char *message, GaimConvImFlags imflags) {
@@ -6624,23 +6658,31 @@
 	oscar_free_name_data(data);
 }
 
-static void oscar_buddycb_edit_comment(GaimConnection *gc, const char *name) {
-	OscarData *od = gc->proto_data;
-	struct name_data *data = g_new(struct name_data, 1);
-	GaimBuddy *b;
+static void oscar_buddycb_edit_comment(GaimBlistNode *node, gpointer ignore) {
+
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+	OscarData *od;
+	struct name_data *data;
 	GaimGroup *g;
 	char *comment;
 	gchar *comment_utf8;
 
-	if (!(b = gaim_find_buddy(gaim_connection_get_account(gc), name)))
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+	od = gc->proto_data;
+
+	data = g_new(struct name_data, 1);
+
+	if (!(g = gaim_find_buddys_group(buddy)))
 		return;
-	if (!(g = gaim_find_buddys_group(b)))
-		return;
-	comment = aim_ssi_getcomment(od->sess->ssi.local, g->name, name);
+	comment = aim_ssi_getcomment(od->sess->ssi.local, g->name, buddy->name);
 	comment_utf8 = comment ? gaim_utf8_try_convert(comment) : NULL;
 
 	data->gc = gc;
-	data->name = g_strdup(name);
+	data->name = g_strdup(buddy->name);
 	data->nick = NULL;
 
 	gaim_request_input(gc, NULL, _("Buddy Comment:"), NULL,
@@ -6653,74 +6695,74 @@
 	g_free(comment_utf8);
 }
 
-static GList *oscar_buddy_menu(GaimConnection *gc, const char *who) {
+static GList *oscar_buddy_menu(GaimBuddy *buddy) {
+
+	GaimConnection *gc = gaim_account_get_connection(buddy->account);
 	OscarData *od = gc->proto_data;
+
 	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Edit Buddy Comment");
-	pbm->callback = oscar_buddycb_edit_comment;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
+	GaimBlistNodeAction *act;
+
+	act = gaim_blist_node_action_new(_("Edit Buddy Comment"),
+			oscar_buddycb_edit_comment, NULL);
+	m = g_list_append(m, act);
 
 	if (od->icq) {
 #if 0
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("Get Status Msg");
-		pbm->callback = oscar_get_icqstatusmsg;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		act = gaim_blist_node_action_new(_("Get Status Msg"),
+				oscar_get_icqstatusmsg, NULL);
+		m = g_list_append(m, act);
 #endif
 	} else {
-		GaimBuddy *b = gaim_find_buddy(gc->account, who);
 		aim_userinfo_t *userinfo;
-
-		if (b)
-			userinfo = aim_locate_finduserinfo(od->sess, b->name);
-
-		if (b && userinfo && aim_sncmp(gaim_account_get_username(gaim_connection_get_account(gc)), who) && GAIM_BUDDY_IS_ONLINE(b)) {
+		userinfo = aim_locate_finduserinfo(od->sess, buddy->name);
+
+		if (userinfo && aim_sncmp(gaim_account_get_username(buddy->account), buddy->name) &&
+				GAIM_BUDDY_IS_ONLINE(buddy)) {
+
 			if (userinfo->capabilities & AIM_CAPS_DIRECTIM) {
-				pbm = g_new0(struct proto_buddy_menu, 1);
-				pbm->label = _("Direct IM");
-				pbm->callback = oscar_ask_direct_im;
-				pbm->gc = gc;
-				m = g_list_append(m, pbm);
+				act = gaim_blist_node_action_new(_("Direct IM"),
+						oscar_ask_direct_im, NULL);
+				m = g_list_append(m, act);
 			}
 
 			if (userinfo->capabilities & AIM_CAPS_SENDFILE) {
-				pbm = g_new0(struct proto_buddy_menu, 1);
-				pbm->label = _("Send File");
-				pbm->callback = oscar_ask_sendfile;
-				pbm->gc = gc;
-				m = g_list_append(m, pbm);
+				act = gaim_blist_node_action_new(_("Send File"),
+						oscar_ask_sendfile, NULL);
+				m = g_list_append(m, act);
 			}
 #if 0
 			if (userinfo->capabilities & AIM_CAPS_GETFILE) {
-				pbm = g_new0(struct proto_buddy_menu, 1);
-				pbm->label = _("Get File");
-				pbm->callback = oscar_ask_getfile;
-				pbm->gc = gc;
-				m = g_list_append(m, pbm);
+				act = gaim_blist_node_action_new(_("Get File"),
+						oscar_ask_getfile, NULL);
+				m = g_list_append(m, act);
 			}
 #endif
 		}
 	}
 
 	if (od->sess->ssi.received_data) {
-		char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, who);
-		if (gname && aim_ssi_waitingforauth(od->sess->ssi.local, gname, who)) {
-			pbm = g_new0(struct proto_buddy_menu, 1);
-			pbm->label = _("Re-request Authorization");
-			pbm->callback = gaim_auth_sendrequest;
-			pbm->gc = gc;
-			m = g_list_append(m, pbm);
+		char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, buddy->name);
+		if (gname && aim_ssi_waitingforauth(od->sess->ssi.local, gname, buddy->name)) {
+			act = gaim_blist_node_action_new(_("Re-request Authorization"),
+					gaim_auth_sendrequest_menu, NULL);
+			m = g_list_append(m, act);
 		}
 	}
 	
 	return m;
 }
 
+
+static GList *oscar_blist_node_menu(GaimBlistNode *node) {
+	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		return oscar_buddy_menu((GaimBuddy *) node);
+	} else {
+		return NULL;
+	}
+}
+
+
 static void oscar_format_screenname(GaimConnection *gc, const char *nick) {
 	OscarData *od = gc->proto_data;
 	if (!aim_sncmp(gaim_account_get_username(gaim_connection_get_account(gc)), nick)) {
@@ -7072,7 +7114,7 @@
 	oscar_status_text,
 	oscar_tooltip_text,
 	oscar_away_states,
-	oscar_buddy_menu,
+	oscar_blist_node_menu,
 	oscar_chat_info,
 	oscar_login,
 	oscar_close,
@@ -7122,7 +7164,6 @@
 	NULL,
 	NULL,
 	NULL,
-	NULL,
 	NULL
 };
 
--- a/src/protocols/silc/buddy.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/silc/buddy.c	Sun May 23 17:27:45 2004 +0000
@@ -314,21 +314,27 @@
 /**************************** Static IM Key **********************************/
 
 static void
-silcgaim_buddy_resetkey(GaimConnection *gc, const char *name)
+silcgaim_buddy_resetkey(GaimBlistNode *node, gpointer data)
 {
-        SilcGaim sg = gc->proto_data;
+	GaimBuddy *b;
+	GaimConnection *gc;
+        SilcGaim sg;
 	char *nickname;
 	SilcClientEntry *clients;
 	SilcUInt32 clients_count;
 
-	if (!name)
-		return;
-	if (!silc_parse_userfqdn(name, &nickname, NULL))
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	b = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(b->account);
+	sg = gc->proto_data;
+
+	if (!silc_parse_userfqdn(b->name, &nickname, NULL))
 		return;
 
 	/* Find client entry */
 	clients = silc_client_get_clients_local(sg->client, sg->conn,
-						nickname, name,
+						nickname, b->name,
 						&clients_count);
 	if (!clients) {
 		silc_free(nickname);
@@ -411,27 +417,33 @@
 }
 
 static void
-silcgaim_buddy_privkey(GaimConnection *gc, const char *name)
+silcgaim_buddy_privkey(GaimBlistNode *node, gpointer data)
 {
-        SilcGaim sg = gc->proto_data;
+	GaimBuddy *b;
+	GaimConnection *gc;
+        SilcGaim sg;
 	char *nickname;
 	SilcGaimPrivkey p;
 	SilcClientEntry *clients;
 	SilcUInt32 clients_count;
 
-	if (!name)
-		return;
-	if (!silc_parse_userfqdn(name, &nickname, NULL))
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	b = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(b->account);
+	sg = gc->proto_data;
+
+	if (!silc_parse_userfqdn(b->name, &nickname, NULL))
 		return;
 
 	/* Find client entry */
 	clients = silc_client_get_clients_local(sg->client, sg->conn,
-						nickname, name,
+						nickname, b->name,
 						&clients_count);
 	if (!clients) {
 		silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
 					silcgaim_buddy_privkey_resolved,
-					g_strdup(name));
+					g_strdup(b->name));
 		silc_free(nickname);
 		return;
 	}
@@ -522,29 +534,36 @@
 }
 
 static void
-silcgaim_buddy_getkey(GaimConnection *gc, const char *name)
+silcgaim_buddy_getkey(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
-	SilcClient client = sg->client;
-	SilcClientConnection conn = sg->conn;
+	GaimBuddy *b;
+	GaimConnection *gc;
+	SilcGaim sg;
+	SilcClient client;
+	SilcClientConnection;
 	SilcClientEntry *clients;
 	SilcUInt32 clients_count;
 	SilcGaimBuddyGetkey g;
 	char *nickname;
 
-	if (!name)
-		return;
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
 
-	if (!silc_parse_userfqdn(name, &nickname, NULL))
+	b = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(b->account);
+	sg = gc->proto_data;
+	client = sg->client;
+	conn = sg->conn;
+
+	if (!silc_parse_userfqdn(b->name, &nickname, NULL))
 		return;
 
 	/* Find client entry */
-	clients = silc_client_get_clients_local(client, conn, nickname, name,
-						&clients_count);
+	clients = silc_client_get_clients_local(client, conn, nickname,
+			b->name, &clients_count);
 	if (!clients) {
 		silc_client_get_clients(client, conn, nickname, NULL,
 					silcgaim_buddy_getkey_resolved,
-					g_strdup(name));
+					g_strdup(b->name));
 		silc_free(nickname);
 		return;
 	}
@@ -566,18 +585,21 @@
 }
 
 static void
-silcgaim_buddy_showkey(GaimConnection *gc, const char *name)
+silcgaim_buddy_showkey(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimBuddy *b;
+	GaimConnection *gc;
+	SilcGaim sg;
 	SilcPublicKey public_key;
 	const char *pkfile;
-	GaimBuddy *b;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
 
-	b = gaim_find_buddy(gc->account, name);
-	if (!b)
-		return;
+	b = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(b->account);
+	sg = gc->proto_data;
 
-	pkfile = gaim_blist_node_get_string((GaimBlistNode *)b, "public-key");
+	pkfile = gaim_blist_node_get_string(buddy, "public-key");
 	if (!silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_PEM) &&
 	    !silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_BIN)) {
 		gaim_notify_error(gc,
@@ -586,7 +608,7 @@
 		return;
 	}
 
-	silcgaim_show_public_key(sg, name, public_key, NULL, NULL);
+	silcgaim_show_public_key(sg, b->name, public_key, NULL, NULL);
 	silc_pkcs_public_key_free(public_key);
 }
 
@@ -1536,87 +1558,87 @@
 }
 
 static void
-silcgaim_buddy_kill(GaimConnection *gc, const char *name)
+silcgaim_buddy_kill(GaimBlistNode *buddy, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
-	SilcClient client = sg->client;
-	SilcClientConnection conn = sg->conn;
+	GaimBuddy *b;
+	GaimConnection *gc;
+	SilcGaim sg;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	b = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(b->account);
+	sg = gc->proto_data;
 
 	/* Call KILL */
-	silc_client_command_call(client, conn, NULL, "KILL",
-				 name, "Killed by operator", NULL);
+	silc_client_command_call(sg->client, sg->conn, NULL, "KILL",
+				 b->name, "Killed by operator", NULL);
 }
 
 static void
-silcgaim_buddy_send_file(GaimConnection *gc, const char *name)
+silcgaim_buddy_send_file(GaimBlistNode *buddy, gpointer data)
 {
-	silcgaim_ftp_send_file(gc, name);
+	GaimBuddy *b;
+	GaimConnection *gc;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	b = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(b->account);
+
+	silcgaim_ftp_send_file(gc, b->name);
 }
 
-GList *silcgaim_buddy_menu(GaimConnection *gc, const char *name)
+GList *silcgaim_buddy_menu(GaimBuddy *buddy)
 {
+
+	GaimConnection *gc = gaim_account_get_connection(buddy->account);
 	SilcGaim sg = gc->proto_data;
 	SilcClientConnection conn = sg->conn;
-	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
-	GaimBuddy *b;
 	const char *pkfile = NULL;
 	SilcClientEntry client_entry = NULL;
+	GaimBlistNodeAction *act;
+	GList *m = NULL;
 
-	b = gaim_find_buddy(gc->account, name);
-	if (b) {
-		pkfile = gaim_blist_node_get_string((GaimBlistNode *)b, "public-key");
-		client_entry = silc_client_get_client_by_id(sg->client,
-							    sg->conn,
-							    b->proto_data);
-	}
+	pkfile = gaim_blist_node_get_string(node, "public-key");
+	client_entry = silc_client_get_client_by_id(sg->client,
+						    sg->conn,
+						    b->proto_data);
 
 	if (client_entry && client_entry->send_key) {
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("Reset IM Key");
-		pbm->callback = silcgaim_buddy_resetkey;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		act = gaim_blist_node_action_new(_("Reset IM Key"),
+				silcgaim_buddy_resetkey);
+		m = g_list_append(m, act);
+
 	} else {
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("IM with Key Exchange");
-		pbm->callback = silcgaim_buddy_keyagr;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		act = gaim_blist_node_action_new(_("IM with Key Exchange"),
+				silcgaim_buddy_keyagr);
+		m = g_list_append(m, act);
 
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("IM with Password");
-		pbm->callback = silcgaim_buddy_privkey;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		act = gaim_blist_node_action_new(_("IM with Password"),
+				silcgaim_buddy_privkey);
+		m = g_list_append(m, act);
 	}
 
 	if (pkfile) {
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("Show Public Key");
-		pbm->callback = silcgaim_buddy_showkey;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		act = gaim_blist_node_action_new(_("Show Public Key"),
+				silcgaim_buddy_showkey);
+		m = g_list_append(m, act);
+
 	} else {
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("Get Public Key...");
-		pbm->callback = silcgaim_buddy_getkey;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		act = gaim_blist_node_action_new(_("Get Public Key..."),
+				silcgaim_buddy_getkey);
+		m = g_list_append(m, act);
 	}
 
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Send File...");
-	pbm->callback = silcgaim_buddy_send_file;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
+	act = gaim_blist_node_action_new(_("Send File..."),
+			silcgaim_buddy_send_file);
+	m = g_list_append(m, act);
 
 	if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) {
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("Kill User");
-		pbm->callback = silcgaim_buddy_kill;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		act = gaim_blist_node_action_new(_("Kill User"),
+				silcgaim_buddy_kill);
+		m = g_list_append(m, act);
 	}
 
 	return m;
--- a/src/protocols/silc/chat.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/silc/chat.c	Sun May 23 17:27:45 2004 +0000
@@ -43,7 +43,7 @@
 }
 
 static void
-silcgaim_chat_getinfo(GaimConnection *gc, GHashTable *components);
+silcgaim_chat_getinfo(GaimBlistNode *node, gpointer data);
 
 static void
 silcgaim_chat_getinfo_res(SilcClient client,
@@ -73,9 +73,12 @@
 }
 
 static void
-silcgaim_chat_getinfo(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_getinfo(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
+
 	const char *chname;
 	char *buf, tmp[256];
 	GString *s;
@@ -83,10 +86,13 @@
 	SilcHashTableList htl;
 	SilcChannelUser chu;
 
-	if (!components)
-		return;
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
 
-	chname = g_hash_table_lookup(components, "channel");
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
+	chname = g_hash_table_lookup(chat->components, "channel");
 	if (!chname)
 		return;
 	channel = silc_client_get_channel(sg->client, sg->conn,
@@ -95,7 +101,7 @@
 		silc_client_get_channel_resolve(sg->client, sg->conn,
 						(char *)chname,
 						silcgaim_chat_getinfo_res,
-						components);
+						chat->components);
 		return;
 	}
 
@@ -162,7 +168,7 @@
 /************************** Channel Invite List ******************************/
 
 static void
-silcgaim_chat_invitelist(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_invitelist(GaimBlistNode *node, gpointer data);
 {
 
 }
@@ -171,7 +177,7 @@
 /**************************** Channel Ban List *******************************/
 
 static void
-silcgaim_chat_banlist(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_banlist(GaimBlistNode *node, gpointer data);
 {
 
 }
@@ -440,11 +446,20 @@
 }
 
 static void
-silcgaim_chat_chauth(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_chauth(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
+
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(components, "channel"),
+				 g_hash_table_lookup(chat->components, "channel"),
 				 "+C", NULL);
 }
 
@@ -513,21 +528,30 @@
 }
 
 static void
-silcgaim_chat_prv(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_prv(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
+
 	SilcGaimCharPrv p;
 	GaimRequestFields *fields;
 	GaimRequestFieldGroup *g;
 	GaimRequestField *f;
 	char tmp[512];
 
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
+
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
 	p = silc_calloc(1, sizeof(*p));
 	if (!p)
 		return;
 	p->sg = sg;
 
-	p->channel = g_hash_table_lookup(components, "channel");
+	p->channel = g_hash_table_lookup(chat->components, "channel");
 	p->c = gaim_blist_find_chat(sg->account, p->channel);
 
 	fields = gaim_request_fields_new();
@@ -559,20 +583,37 @@
 /****************************** Channel Modes ********************************/
 
 static void
-silcgaim_chat_permanent_reset(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_permanent_reset(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
+
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(components, "channel"),
+				 g_hash_table_lookup(chat->components, "channel"),
 				 "-f", NULL);
 }
 
 static void
-silcgaim_chat_permanent(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_permanent(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
 	const char *channel;
 
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
+
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
 	if (!sg->conn)
 		return;
 
@@ -581,7 +622,7 @@
 	   (default key). */
 
 	/* Call CMODE */
-	channel = g_hash_table_lookup(components, "channel");
+	channel = g_hash_table_lookup(chat->components, "channel");
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", channel,
 				 "+f", NULL);
 }
@@ -629,18 +670,27 @@
 }
 
 static void
-silcgaim_chat_ulimit(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_ulimit(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
+
 	SilcGaimChatInput s;
 	SilcChannelEntry channel;
 	const char *ch;
 	char tmp[32];
 
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
+
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
 	if (!sg->conn)
 		return;
 
-	ch = g_strdup(g_hash_table_lookup(components, "channel"));
+	ch = g_strdup(g_hash_table_lookup(chat->components, "channel"));
 	channel = silc_client_get_channel(sg->client, sg->conn, (char *)ch);
 	if (!channel)
 		return;
@@ -659,70 +709,127 @@
 }
 
 static void
-silcgaim_chat_resettopic(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_resettopic(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
+
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(components, "channel"),
+				 g_hash_table_lookup(chat->components, "channel"),
 				 "-t", NULL);
 }
 
 static void
-silcgaim_chat_settopic(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_settopic(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
+
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(components, "channel"),
+				 g_hash_table_lookup(chat->components, "channel"),
 				 "+t", NULL);
 }
 
 static void
-silcgaim_chat_resetprivate(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_resetprivate(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
+
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(components, "channel"),
+				 g_hash_table_lookup(chat->components, "channel"),
 				 "-p", NULL);
 }
 
 static void
-silcgaim_chat_setprivate(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_setprivate(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
+
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(components, "channel"),
+				 g_hash_table_lookup(chat->components, "channel"),
 				 "+p", NULL);
 }
 
 static void
-silcgaim_chat_resetsecret(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_resetsecret(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
+
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(components, "channel"),
+				 g_hash_table_lookup(chat->components, "channel"),
 				 "-s", NULL);
 }
 
 static void
-silcgaim_chat_setsecret(GaimConnection *gc, GHashTable *components)
+silcgaim_chat_setsecret(GaimBlistNode *node, gpointer data)
 {
-	SilcGaim sg = gc->proto_data;
+	GaimChat *chat;
+	GaimConnection *gc;
+	SilcGaim sg;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node));
+
+	chat = (GaimChat *) node;
+	gc = gaim_account_get_connection(chat->account);
+	sg = gc->proto_data;
+
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(components, "channel"),
+				 g_hash_table_lookup(chat->components, "channel"),
 				 "+s", NULL);
 }
 
-GList *silcgaim_chat_menu(GaimConnection *gc, GHashTable *components)
+GList *silcgaim_chat_menu(GaimChat *chat)
 {
+	GHashTable components = chat->components;
+	GaimConnection *gc = gaim_account_get_connection(chat->account);
 	SilcGaim sg = gc->proto_data;
 	SilcClientConnection conn = sg->conn;
-	GList *m = NULL;
-	struct proto_chat_menu *pcm;
 	const char *chname = NULL;
 	SilcChannelEntry channel = NULL;
 	SilcChannelUser chu = NULL;
 	SilcUInt32 mode = 0;
 
+	GList *m;
+	GaimBlistNodeAction *act;
+
 	if (components)
 		chname = g_hash_table_lookup(components, "channel");
 	if (chname)
@@ -737,106 +844,78 @@
 	if (strstr(chname, "[Private Group]"))
 		return NULL;
 
-	pcm = g_new0(struct proto_chat_menu, 1);
-	pcm->label = _("Get Info");
-	pcm->callback = silcgaim_chat_getinfo;
-	pcm->gc = gc;
-	m = g_list_append(m, pcm);
+	act = gaim_blist_node_action_new(_("Get Info"),
+			silcgaim_chat_getinfo, NULL);
+	m = g_list_append(m, act);
 
 #if 0   /* XXX For now these are not implemented.  We need better
 	   listview dialog from Gaim for these. */
 	if (mode & SILC_CHANNEL_UMODE_CHANOP) {
-		pcm = g_new0(struct proto_chat_menu, 1);
-		pcm->label = _("Invite List");
-		pcm->callback = silcgaim_chat_invitelist;
-		pcm->gc = gc;
-		m = g_list_append(m, pcm);
+		act = gaim_blist_node_action_new(_("Invite List"),
+				silcgaim_chat_invitelist, NULL);
+		m = g_list_append(m, act);
 
-		pcm = g_new0(struct proto_chat_menu, 1);
-		pcm->label = _("Ban List");
-		pcm->callback = silcgaim_chat_banlist;
-		pcm->gc = gc;
-		m = g_list_append(m, pcm);
+		act = gaim_blist_node_action_new(_("Ban List"),
+				silcgaim_chat_banlist, NULL);
+		m = g_list_append(m, act);
 	}
 #endif
 
 	if (chu) {
-		pcm = g_new0(struct proto_chat_menu, 1);
-		pcm->label = _("Add Private Group");
-		pcm->callback = silcgaim_chat_prv;
-		pcm->gc = gc;
-		m = g_list_append(m, pcm);
+		act = gaim_blist_node_action_new(_("Add Private Group"),
+				silcgaim_chat_prv, NULL);
+		m = g_list_append(m, act);
 	}
 
 	if (mode & SILC_CHANNEL_UMODE_CHANFO) {
-		pcm = g_new0(struct proto_chat_menu, 1);
-		pcm->label = _("Channel Authentication");
-		pcm->callback = silcgaim_chat_chauth;
-		pcm->gc = gc;
-		m = g_list_append(m, pcm);
+		act = gaim_blist_node_action_new(_("Channel Authentication"),
+				silcgaim_chat_chauth, NULL);
+		m = g_list_append(m, act);
 
 		if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
-			pcm = g_new0(struct proto_chat_menu, 1);
-			pcm->label = _("Reset Permanent");
-			pcm->callback = silcgaim_chat_permanent_reset;
-			pcm->gc = gc;
-			m = g_list_append(m, pcm);
+			act = gaim_blist_node_action_new(_("Reset Permanent"),
+					silcgaim_chat_permanent_reset, NULL);
+			m = g_list_append(m, act);
 		} else {
-			pcm = g_new0(struct proto_chat_menu, 1);
-			pcm->label = _("Set Permanent");
-			pcm->callback = silcgaim_chat_permanent;
-			pcm->gc = gc;
-			m = g_list_append(m, pcm);
+			act = gaim_blist_node_action_new(_("Set Permanent"),
+					silcgaim_chat_permanent, NULL);
+			m = g_list_append(m, act);
 		}
 	}
 
 	if (mode & SILC_CHANNEL_UMODE_CHANOP) {
-		pcm = g_new0(struct proto_chat_menu, 1);
-		pcm->label = _("Set User Limit");
-		pcm->callback = silcgaim_chat_ulimit;
-		pcm->gc = gc;
-		m = g_list_append(m, pcm);
+		act = gaim_blist_node_action_new(_("Set User Limit"),
+				silcgaim_chat_ulimit, NULL);
+		m = g_list_append(m, act);
 
 		if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
-			pcm = g_new0(struct proto_chat_menu, 1);
-			pcm->label = _("Reset Topic Restriction");
-			pcm->callback = silcgaim_chat_resettopic;
-			pcm->gc = gc;
-			m = g_list_append(m, pcm);
+			act = gaim_blist_node_action_new(_("Reset Topic Restriction"),
+					silcgaim_chat_resettopic, NULL);
+			m = g_list_append(m, act);
 		} else {
-			pcm = g_new0(struct proto_chat_menu, 1);
-			pcm->label = _("Set Topic Restriction");
-			pcm->callback = silcgaim_chat_settopic;
-			pcm->gc = gc;
-			m = g_list_append(m, pcm);
+			act = gaim_blist_node_action_new(_("Set Topic Restriction"),
+					silcgaim_chat_settopic, NULL);
+			m = g_list_append(m, act);
 		}
 
 		if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) {
-			pcm = g_new0(struct proto_chat_menu, 1);
-			pcm->label = _("Reset Private Channel");
-			pcm->callback = silcgaim_chat_resetprivate;
-			pcm->gc = gc;
-			m = g_list_append(m, pcm);
+			act = gaim_blist_node_action_new(_("Reset Private Channel"),
+					silcgaim_chat_resetprivate, NULL);
+			m = g_list_append(m, act);
 		} else {
-			pcm = g_new0(struct proto_chat_menu, 1);
-			pcm->label = _("Set Private Channel");
-			pcm->callback = silcgaim_chat_setprivate;
-			pcm->gc = gc;
-			m = g_list_append(m, pcm);
+			act = gaim_blist_node_action_new(_("Set Private Channel"),
+					silcgaim_chat_setprivate, NULL);
+			m = g_list_append(m, act);
 		}
 
 		if (channel->mode & SILC_CHANNEL_MODE_SECRET) {
-			pcm = g_new0(struct proto_chat_menu, 1);
-			pcm->label = _("Reset Secret Channel");
-			pcm->callback = silcgaim_chat_resetsecret;
-			pcm->gc = gc;
-			m = g_list_append(m, pcm);
+			act = gaim_blist_node_action_new(_("Reset Secret Channel"),
+					silcgaim_chat_resetsecret, NULL);
+			m = g_list_append(m, act);
 		} else {
-			pcm = g_new0(struct proto_chat_menu, 1);
-			pcm->label = _("Set Secret Channel");
-			pcm->callback = silcgaim_chat_setsecret;
-			pcm->gc = gc;
-			m = g_list_append(m, pcm);
+			act = gaim_blist_node_action_new(_("Set Secret Channel"),
+					silcgaim_chat_setsecret, NULL);
+			m = g_list_append(m, act);
 		}
 	}
 
--- a/src/protocols/silc/silc.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/silc/silc.c	Sun May 23 17:27:45 2004 +0000
@@ -880,6 +880,20 @@
 }
 
 
+GList *silcgaim_blist_node_menu(GaimBlistNode *node) {
+	/* split this single menu building function back into the two
+	   original: one for buddies and one for chats */
+
+	if(GAIM_BLIST_NODE_IS_CHAT(node)) {
+		return silcgaim_chat_menu((GaimChar *) node);
+	} else if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		return silcgaim_buddy_menu((GaimBuddy *) node);
+	} else {
+		return_val_if_reached(NULL);
+	}	
+}
+
+
 /************************** Plugin Initialization ****************************/
 
 static GaimPluginPrefFrame *
@@ -949,7 +963,7 @@
 	silcgaim_status_text,
 	silcgaim_tooltip_text,
 	silcgaim_away_states,
-	silcgaim_buddy_menu,
+	silcgaim_blist_node_menu,
 	silcgaim_chat_info,
 	silcgaim_login,
 	silcgaim_close,
@@ -993,8 +1007,7 @@
 	NULL,
 	silcgaim_roomlist_get_list,
 	silcgaim_roomlist_cancel,
-	NULL,
-	silcgaim_chat_menu
+	NULL
 };
 
 static GaimPluginInfo info =
--- a/src/protocols/toc/toc.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/toc/toc.c	Sun May 23 17:27:45 2004 +0000
@@ -25,7 +25,6 @@
 #include "conversation.h"
 #include "debug.h"
 #include "prpl.h"
-#include "multi.h"
 #include "notify.h"
 #include "proxy.h"
 #include "request.h"
@@ -1119,10 +1118,19 @@
 }
 
 /* Should be implemented as an Account Action? */
-static void toc_get_dir(GaimConnection *gc, const char *name)
+static void toc_get_dir(GaimBlistNode *node, gpointer data)
 {
+	GaimBuddy *buddy;
+	GaimConnection *gc;
 	char buf[BUF_LEN * 2];
-	g_snprintf(buf, MSG_LEN, "toc_get_dir %s", gaim_normalize(gc->account, name));
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_acount_get_connection(buddy->account);
+
+	g_snprintf(buf, MSG_LEN, "toc_get_dir %s",
+			gaim_normalize(buddy->account, buddy->name));
 	sflap_send(gc, buf, -1, TYPE_DATA);
 }
 
@@ -1400,16 +1408,16 @@
 	*ne = emblems[3];
 }
 
-static GList *toc_buddy_menu(GaimConnection *gc, const char *who)
+static GList *toc_blist_node_menu(GaimBlistNode *node)
 {
 	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
+	GaimBlistNodeAction *act;
 
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Get Dir Info");
-	pbm->callback = toc_get_dir;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
+	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		act = gaim_blist_node_action_new(_("Get Dir Info"),
+				toc_get_dir, NULL);
+		m = g_list_append(m, act);
+	}
 
 	return m;
 }
@@ -2103,7 +2111,7 @@
 	NULL,
 	NULL,
 	toc_away_states,
-	toc_buddy_menu,
+	toc_blist_node_menu,
 	toc_chat_info,
 	toc_login,
 	toc_close,
@@ -2147,7 +2155,6 @@
 	NULL,
 	NULL,
 	NULL,
-	NULL,
 	NULL
 };
 
--- a/src/protocols/trepia/trepia.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/trepia/trepia.c	Sun May 23 17:27:45 2004 +0000
@@ -34,7 +34,6 @@
 
 /* XXX */
 #include "gaim.h"
-#include "multi.h"
 
 #ifndef _WIN32
 # include <sys/socket.h>
@@ -444,37 +443,38 @@
 }
 
 static void
-trepia_visit_homepage(GaimConnection *gc, const char *who)
+trepia_visit_homepage(GaimBlistNode *node, gpointer data)
 {
+	GaimBuddy *buddy;
+	GaimConnection *gc;
 	TrepiaProfile *profile;
-	GaimBuddy *b;
 	const char *value;
 
-	b = gaim_find_buddy(gaim_connection_get_account(gc), who);
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
 	profile = b->proto_data;
+	value = trepia_profile_get_homepage(profile);
 
-	if ((value = trepia_profile_get_homepage(profile)) != NULL)
+	if (value != NULL)
 		gaim_notify_uri(gc, value);
 }
 
 static GList *
-trepia_buddy_menu(GaimConnection *gc, const char *who)
+trepia_blist_node_menu(GaimBlistNode *node)
 {
-	TrepiaProfile *profile;
-	GaimBuddy *b;
-	const char *value = NULL;
 	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
+	GaimBlistNodeACtion *act;
 
-	b = gaim_find_buddy(gaim_connection_get_account(gc), who);
-	profile = b->proto_data;
+	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		TrepiaProfile *profile = buddy->proto_data;
 
-	if ((value = trepia_profile_get_homepage(profile)) != NULL) {
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("Visit Homepage");
-		pbm->callback = trepia_visit_homepage;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		if (trepia_profile_get_homepage(profile) != NULL) {
+			act = gaim_blist_node_action_new(_("Visit Homepage"),
+					trepia_visit_homepage, NULL);
+			m = g_list_append(m, act);
+		}
 	}
 
 	return m;
@@ -1214,7 +1214,7 @@
 	trepia_status_text,
 	trepia_tooltip_text,
 	NULL,
-	trepia_buddy_menu,
+	trepia_blist_node_menu,
 	NULL,
 	trepia_login,
 	trepia_close,
--- a/src/protocols/yahoo/yahoo.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/yahoo/yahoo.c	Sun May 23 17:27:45 2004 +0000
@@ -26,7 +26,6 @@
 #include "accountopt.h"
 #include "blist.h"
 #include "debug.h"
-#include "multi.h"
 #include "notify.h"
 #include "privacy.h"
 #include "prpl.h"
@@ -2303,12 +2302,19 @@
 	}
 }
 
-static void yahoo_initiate_conference(GaimConnection *gc, const char *name)
-{
+static void yahoo_initiate_conference(GaimBlistNode *node, gpointer data) {
+
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+
 	GHashTable *components;
 	struct yahoo_data *yd;
 	int id;
 
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
 	yd = gc->proto_data;
 	id = yd->conf_id;
 
@@ -2320,17 +2326,27 @@
 	yahoo_c_join(gc, components);
 	g_hash_table_destroy(components);
 
-	yahoo_c_invite(gc, id, "Join my conference...", name);
+	yahoo_c_invite(gc, id, "Join my conference...", buddy->name);
 }
 
-static void yahoo_game(GaimConnection *gc, const char *name) {
-	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
+static void yahoo_game(GaimBlistNode *node, gpointer data) {
+
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+
+	struct yahoo_data *yd;
 	char *game = NULL;
 	char *t;
 	char url[256];
 	struct yahoo_friend *f;
 
-	f = g_hash_table_lookup(yd->friends, name);
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+	yd = (struct yahoo_data *) gc->proto_data;
+
+	f = g_hash_table_lookup(yd->friends, buddy->name);
 	if (!f)
 		return;
 
@@ -2414,52 +2430,83 @@
 	return ret;
 }
 
-static void yahoo_addbuddyfrommenu_cb(GaimConnection *gc, const char *who)
+static void yahoo_addbuddyfrommenu_cb(GaimBlistNode *node, gpointer data)
+{
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+
+	yahoo_add_buddy(gc, buddy->name, NULL);
+}
+
+
+static void yahoo_chat_goto_menu(GaimBlistNode *node, gpointer data)
 {
-	yahoo_add_buddy(gc, who, NULL);
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+
+	yahoo_chat_goto(gc, buddy->name);
 }
 
-static GList *yahoo_buddy_menu(GaimConnection *gc, const char *who)
+
+static void yahoo_ask_send_file_menu(GaimBlistNode *node, gpointer data)
+{
+	GaimBuddy *buddy;
+	GaimConnection *gc;
+
+	g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (GaimBuddy *) node;
+	gc = gaim_account_get_connection(buddy->account);
+
+	yahoo_ask_send_file(gc, buddy->name);
+}
+
+
+static GList *yahoo_buddy_menu(GaimBuddy *buddy)
 {
 	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
+	GaimBlistNodeAction *act;
+
+	GaimConnection *gc = gaim_account_get_connection(buddy->account);
 	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
 	static char buf2[1024];
 	struct yahoo_friend *f;
 
-	f = g_hash_table_lookup(yd->friends, who);
+	f = g_hash_table_lookup(yd->friends, buddy->name);
 
 	if (!f) {
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("Add Buddy");
-		pbm->callback = yahoo_addbuddyfrommenu_cb;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+		act = gaim_blist_node_action_new(_("Add Buddy"),
+				yahoo_addbuddyfrommenu_cb, NULL);
+		m = g_list_append(m, act);
 
 		return m;
-	}
-
-	if (f->status == YAHOO_STATUS_OFFLINE)
+
+	} else if (f->status == YAHOO_STATUS_OFFLINE) {
 		return NULL;
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Join in Chat");
-	pbm->callback = yahoo_chat_goto;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Initiate Conference");
-	pbm->callback = yahoo_initiate_conference;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
+	}
+
+	act = gaim_blist_node_action_new(_("Join in Chat"),
+			yahoo_chat_goto_menu, NULL);
+	m = g_list_append(m, act);
+
+	act = gaim_blist_node_action_new(_("Initiate Conference"),
+			yahoo_initiate_conference, NULL);
+	m = g_list_append(m, act);
 
 	/* FIXME: remove this when the ui does it for us. */
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Send File");
-	pbm->callback = yahoo_ask_send_file;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
+	act = gaim_blist_node_action_new(_("Send File"),
+			yahoo_ask_send_file_menu, NULL);
+	m = g_list_append(m, act);
 
 	if (f->game) {
 		char *game = f->game;
@@ -2469,7 +2516,6 @@
 		if (!game)
 			return m;
 
-		pbm = g_new0(struct proto_buddy_menu, 1);
 		if (!(room = strstr(game, "&follow="))) /* skip ahead to the url */
 			return m;
 		while (*room && *room != '\t')          /* skip to the tab */
@@ -2479,15 +2525,25 @@
 			t++;                            /* replace the \n with a space */
 		*t = ' ';
 		g_snprintf(buf2, sizeof buf2, "%s", room);
-		pbm->label = buf2;
-		pbm->callback = yahoo_game;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
+
+		act = gaim_blist_node_action_new(buf2, yahoo_game, NULL);
+		m = g_list_append(m, act);
 	}
 
 	return m;
 }
 
+
+static GList *yahoo_blist_node_menu(GaimBlistNode *node)
+{
+	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		return yahoo_buddy_menu((GaimBuddy *) node);
+	} else {
+		return NULL;
+	}
+}
+
+
 static void yahoo_act_id(GaimConnection *gc, const char *entry)
 {
 	struct yahoo_data *yd = gc->proto_data;
@@ -3223,7 +3279,7 @@
 	yahoo_status_text,
 	yahoo_tooltip_text,
 	yahoo_away_states,
-	yahoo_buddy_menu,
+	yahoo_blist_node_menu,
 	yahoo_c_info,
 	yahoo_login,
 	yahoo_close,
@@ -3272,8 +3328,7 @@
 	NULL,
 	yahoo_roomlist_get_list,
 	yahoo_roomlist_cancel,
-	yahoo_roomlist_expand_category,
-	NULL
+	yahoo_roomlist_expand_category
 };
 
 static GaimPluginInfo info =
--- a/src/protocols/zephyr/zephyr.c	Sun May 23 08:06:38 2004 +0000
+++ b/src/protocols/zephyr/zephyr.c	Sun May 23 17:27:45 2004 +0000
@@ -1144,30 +1144,14 @@
 	return buf;
 }
 
-static void zephyr_zloc(GaimConnection * gc, const char *who)
+static void zephyr_zloc(GaimConnection *gc, const char *who)
 {
 	ZAsyncLocateData_t ald;
 
-	if (ZRequestLocations(local_zephyr_normalize(who), &ald, UNACKED, ZAUTH)
-		!= ZERR_NONE) {
-		return;
+	if (ZRequestLocations(local_zephyr_normalize(who), &ald, UNACKED, ZAUTH) == ZERR_NONE) {
+		pending_zloc_names = g_list_append(pending_zloc_names,
+				g_strdup(local_zephyr_normalize(who)));
 	}
-	pending_zloc_names = g_list_append(pending_zloc_names, g_strdup(local_zephyr_normalize(who)));
-}
-
-static GList *zephyr_buddy_menu(GaimConnection * gc, const char *who)
-{
-	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-
-	pbm->label = _("ZLocate");
-	pbm->callback = zephyr_zloc;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-
-	return m;
 }
 
 static void zephyr_set_away(GaimConnection * gc, const char *state, const char *msg)
@@ -1307,7 +1291,7 @@
 	NULL,
 	NULL,
 	zephyr_away_states,
-	zephyr_buddy_menu,
+	NULL,
 	zephyr_chat_info,
 	zephyr_login,
 	zephyr_close,
@@ -1351,7 +1335,6 @@
 	NULL,
 	NULL,
 	NULL,
-	NULL,
 	NULL
 };
 
--- a/src/prpl.h	Sun May 23 08:06:38 2004 +0000
+++ b/src/prpl.h	Sun May 23 17:27:45 2004 +0000
@@ -239,7 +239,7 @@
 
 	GList *(*away_states)(GaimConnection *gc);
 
-	GList *(*buddy_menu)(GaimConnection *, const char *);
+	GList *(*blist_node_menu)(GaimBlistNode *node);
 	GList *(*chat_info)(GaimConnection *);
 
 	/* All the server-related functions */
@@ -316,9 +316,6 @@
 	struct _GaimRoomlist *(*roomlist_get_list)(GaimConnection *gc);
 	void (*roomlist_cancel)(struct _GaimRoomlist *list);
 	void (*roomlist_expand_category)(struct _GaimRoomlist *list, struct _GaimRoomlistRoom *category);
-
-	/* Chat specific menu in the buddy list */
-	GList *(*chat_menu)(GaimConnection *, GHashTable *);
 };
 
 #define GAIM_IS_PROTOCOL_PLUGIN(plugin) \
@@ -327,6 +324,9 @@
 #define GAIM_PLUGIN_PROTOCOL_INFO(plugin) \
 	((GaimPluginProtocolInfo *)(plugin)->info->extra_info)
 
+/* It's not like we're going to run out of integers for this version
+   number, but we only want to really change it once per release. */
+/* GAIM_PRPL_API_VERSION last changed for version: 0.78 */
 #define GAIM_PRPL_API_VERSION 4
 
 #ifdef __cplusplus
--- a/src/roomlist.h	Sun May 23 08:06:38 2004 +0000
+++ b/src/roomlist.h	Sun May 23 17:27:45 2004 +0000
@@ -26,6 +26,11 @@
 #ifndef _GAIM_ROOMLIST_H_
 #define _GAIM_ROOMLIST_H_
 
+
+#include <glib/glist.h>
+#include "account.h"
+
+
 /**************************************************************************/
 /** Data Structures                                                       */
 /**************************************************************************/