changeset 12919:248b8b39c671

[gaim-migrate @ 15272] Replace GaimBlistNodeAction with the more generic GaimMenuAction, this is in preparation for letting the chat room user list have extensible menus like the blist entries do. (I know it's not exactly the prettiest, and the callback isn't exactly type-safe, when we eventually gobjectify everything we can get some safety back by using (GObject, gpointer) but that's for later.) I'm planning to look into merging GaimPluginActions into GaimMenuActions as well. committer: Tailor Script <tailor@pidgin.im>
author Etan Reisner <pidgin@unreliablesource.net>
date Tue, 17 Jan 2006 23:22:19 +0000
parents a05fbd9dcc31
children 1577b8e194de
files plugins/ChangeLog.API plugins/gevolution/gevolution.c plugins/perl/common/BuddyList.xs plugins/perl/common/module.h plugins/perl/common/typemap src/blist.c src/blist.h src/gtkblist.c src/gtkutils.c src/gtkutils.h src/protocols/gg/gg.c src/protocols/jabber/buddy.c src/protocols/msn/msn.c src/protocols/novell/novell.c src/protocols/oscar/oscar.c src/protocols/sametime/sametime.c src/protocols/silc/buddy.c src/protocols/silc/chat.c src/protocols/toc/toc.c src/protocols/yahoo/yahoo.c src/util.c src/util.h
diffstat 22 files changed, 295 insertions(+), 266 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/ChangeLog.API	Tue Jan 17 19:28:45 2006 +0000
+++ b/plugins/ChangeLog.API	Tue Jan 17 23:22:19 2006 +0000
@@ -144,6 +144,7 @@
 	  so GAIM_TYPE_BOXED is used for the signal types
 	* gaim_gtk_privacy_is_showable(): We do fallback privacy in the core
 	  now, so this would always be TRUE now.
+	* gaim_blist_node_action_new9); use gaim_menu_action_new() instead
 
 	Added:
 	* gaim_prefs_disconnect_by_handle()
@@ -238,6 +239,7 @@
 	* gaim_gtk_log_get_handle()
 	* gaim_gtk_log_uninit()
 	* gaim_url_fetch_request()
+	* gaim_menu_action_new()
 
 	Signals - Changed:  (See the Doxygen docs for details on all signals.)
 	* Signal propagation now stops after a handler returns a non-NULL value.
--- a/plugins/gevolution/gevolution.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/plugins/gevolution/gevolution.c	Tue Jan 17 23:22:19 2006 +0000
@@ -273,7 +273,7 @@
 static void
 blist_node_extended_menu_cb(GaimBlistNode *node, GList **menu)
 {
-	GaimBlistNodeAction *act;
+	GaimMenuAction *act;
 	GaimBuddy *buddy;
 
 	if (!GAIM_BLIST_NODE_IS_BUDDY(node))
@@ -283,13 +283,13 @@
 
 	if (gevo_prpl_is_supported(buddy->account, buddy))
 	{
-		act = gaim_blist_node_action_new(_("Add to Address Book"),
-		                                 menu_item_activate_cb,
-		                                 NULL, NULL);
+		act = gaim_menu_action_new(_("Add to Address Book"),
+		                           GAIM_CALLBACK(menu_item_activate_cb),
+		                           NULL, NULL);
 		*menu = g_list_append(*menu, act);
-		act = gaim_blist_node_action_new(_("Send E-Mail"),
-		                                 menu_item_send_mail_activate_cb,
-		                                 NULL, NULL);
+		act = gaim_menu_action_new(_("Send E-Mail"),
+		                           GAIM_CALLBACK(menu_item_send_mail_activate_cb),
+		                           NULL, NULL);
 		*menu = g_list_append(*menu, act);
 	}
 }
--- a/plugins/perl/common/BuddyList.xs	Tue Jan 17 19:28:45 2006 +0000
+++ b/plugins/perl/common/BuddyList.xs	Tue Jan 17 23:22:19 2006 +0000
@@ -222,10 +222,7 @@
 	GList *l;
 PPCODE:
 	for (l = gaim_blist_node_get_extended_menu(node); l != NULL; l = l->next) {
-		/* XXX I'm pretty sure this is what should be being used here,
-		 * not that it really matters since perl doesn't really have a
-		 * way to interact with a GaimBlistNodeAction anyway. */
-		XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::BuddyList::Node::Action")));
+		XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Menu::Action")));
 	}
 
 void
--- a/plugins/perl/common/module.h	Tue Jan 17 19:28:45 2006 +0000
+++ b/plugins/perl/common/module.h	Tue Jan 17 23:22:19 2006 +0000
@@ -65,7 +65,6 @@
 
 /* blist.h */
 typedef GaimBlistNode *			Gaim__BuddyList__Node;
-typedef GaimBlistNodeAction *		Gaim__BuddyList__Node__Action;
 typedef GaimBlistNodeFlags		Gaim__BuddyList__NodeFlags;
 typedef GaimBlistUiOps *		Gaim__BuddyList__UiOps;
 typedef GaimBuddyList *			Gaim__BuddyList;
@@ -246,6 +245,9 @@
 
 typedef GaimStringref *			Gaim__Stringref;
 
+/* util.h */
+typedef GaimMenuAction *		Gaim__Menu__Action;
+
 /* value.h */
 typedef GaimValue *			Gaim__Value;
 
--- a/plugins/perl/common/typemap	Tue Jan 17 19:28:45 2006 +0000
+++ b/plugins/perl/common/typemap	Tue Jan 17 23:22:19 2006 +0000
@@ -50,7 +50,6 @@
 Gaim::BuddyList::Group			T_GaimObj
 Gaim::BuddyList::Node			T_GaimObj
 Gaim::BuddyList::NodeFlags		T_IV
-Gaim::BuddyList::Node::Action		T_GaimObj
 Gaim::BuddyList::UiOps			T_GaimObj
 
 Gaim::Cipher				T_GaimObj
@@ -89,6 +88,8 @@
 Gaim::Log::ReadFlags			T_GaimObj
 Gaim::Log::Set				T_GaimObj
 
+Gaim::Menu::Action			T_GaimObj
+
 Gaim::NetworkListenCallback		T_PTR
 
 Gaim::NotifyCloseCallback		T_PTR
--- a/src/blist.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/blist.c	Tue Jan 17 23:22:19 2006 +0000
@@ -2566,19 +2566,6 @@
 	return menu;
 }
 
-GaimBlistNodeAction *
-gaim_blist_node_action_new(char *label,
-                           void (*callback)(GaimBlistNode *, gpointer),
-                           gpointer data, GList *children)
-{
-	GaimBlistNodeAction *act = g_new0(GaimBlistNodeAction, 1);
-	act->label = label;
-	act->callback = callback;
-	act->data = data;
-	act->children = children;
-	return act;
-}
-
 int gaim_blist_get_group_size(GaimGroup *group, gboolean offline)
 {
 	if (!group)
--- a/src/blist.h	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/blist.h	Tue Jan 17 23:22:19 2006 +0000
@@ -33,8 +33,6 @@
 typedef struct _GaimBlistUiOps GaimBlistUiOps;
 typedef struct _GaimBlistNode GaimBlistNode;
 
-typedef struct _GaimBlistNodeAction GaimBlistNodeAction;
-
 typedef struct _GaimChat GaimChat;
 typedef struct _GaimGroup GaimGroup;
 typedef struct _GaimContact GaimContact;
@@ -184,15 +182,6 @@
 	void (*request_add_group)(void);
 };
 
-
-struct _GaimBlistNodeAction {
-	char *label;
-	void (*callback)(GaimBlistNode *, gpointer);
-	gpointer data;
-	GList *children;
-};
-
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -848,31 +837,14 @@
 
 /*@}*/
 
-
 /**
  * Retrieves the extended menu items for a buddy list node.
  * @param n The blist node for which to obtain the extended menu items.
- * @return  A list of GaimBlistNodeAction items, as harvested by the
+ * @return  A list of GaimMenuAction items, as harvested by the
  *          blist-node-extended-menu signal.
  */
 GList *gaim_blist_node_get_extended_menu(GaimBlistNode *n);
 
-
-/**
- * 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
- * @param children      A GList of GaimBlistNodeActions to be added as a
- *                      submenu of the action.
- * @return              The GaimBlistNodeAction.
- */
-GaimBlistNodeAction *gaim_blist_node_action_new(char *label,
-		void (*callback)(GaimBlistNode *, gpointer), gpointer data,
-		GList *children);
-
-
 /**************************************************************************/
 /** @name UI Registration Functions                                       */
 /**************************************************************************/
--- a/src/gtkblist.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/gtkblist.c	Tue Jan 17 23:22:19 2006 +0000
@@ -905,102 +905,35 @@
 	}
 }
 
-
-static void
-blist_node_menu_cb(GtkMenuItem *item, GaimBlistNode *node)
-{
-	void (*callback)(GaimBlistNode *, gpointer);
-	gpointer data;
-	callback = g_object_get_data(G_OBJECT(item), "gaimcallback");
-	data = g_object_get_data(G_OBJECT(item), "gaimcallbackdata");
-	if (callback)
-		callback(node, data);
-}
-
-
-static void
-append_blist_node_action(GtkWidget *menu, GaimBlistNodeAction *act,
-		GaimBlistNode *node, gboolean *dup_separator)
-{
-	if(act == NULL) {
-		if(! *dup_separator) {
-			gaim_separator(menu);
-			*dup_separator = TRUE;
-		}
-	} else {
-		GtkWidget *menuitem;
-
-		if (act->children == NULL) {
-			*dup_separator = FALSE;
-
-			menuitem = gtk_menu_item_new_with_mnemonic(act->label);
-			if (act->callback != NULL) {
-				g_object_set_data(G_OBJECT(menuitem), "gaimcallback",
-				                  act->callback);
-				g_object_set_data(G_OBJECT(menuitem), "gaimcallbackdata",
-				                  act->data);
-				g_signal_connect(G_OBJECT(menuitem), "activate",
-				                 G_CALLBACK(blist_node_menu_cb), node);
-			} else {
-				gtk_widget_set_sensitive(menuitem, FALSE);
-			}
-			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-		} else {
-			GtkWidget *submenu = NULL;
-			GList *l = NULL;
-
-			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);
-
-			for (l = act->children; l; l = l->next) {
-				GaimBlistNodeAction *act = (GaimBlistNodeAction *) l->data;
-
-				append_blist_node_action(submenu, act, node, dup_separator);
-			}
-			g_list_free(act->children);
-			act->children = NULL;
-		}
-		g_free(act);
-	}
-}
-
-
 void
 gaim_gtk_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);
+		GaimMenuAction *act = (GaimMenuAction *) l->data;
+		gaim_gtk_append_menu_action(menu, act, node);
 	}
 	g_list_free(ll);
 }
 
-
 void
-gaim_gtk_append_blist_node_extended_menu (GtkWidget *menu, GaimBlistNode *node)
+gaim_gtk_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);
+		GaimMenuAction *act = (GaimMenuAction *) l->data;
+		gaim_gtk_append_menu_action(menu, act, node);
 	}
 	g_list_free(ll);
 }
 
-
 void
 gaim_gtk_blist_make_buddy_menu(GtkWidget *menu, GaimBuddy *buddy, gboolean sub) {
 	GaimPluginProtocolInfo *prpl_info;
--- a/src/gtkutils.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/gtkutils.c	Tue Jan 17 23:22:19 2006 +0000
@@ -1623,3 +1623,70 @@
 	return scale;
 }
 
+static void
+menu_action_cb(GtkMenuItem *item, gpointer object)
+{
+	gpointer data;
+	void (*callback)(gpointer, gpointer);
+
+	callback = g_object_get_data(G_OBJECT(item), "gaimcallback");
+	data = g_object_get_data(G_OBJECT(item), "gaimcallbackdata");
+
+	if (callback)
+		callback(object, data);
+}
+
+void
+gaim_gtk_append_menu_action(GtkWidget *menu, GaimMenuAction *act,
+                            gpointer object)
+{
+	if (act == NULL) {
+		gaim_separator(menu);
+	} else {
+		GtkWidget *menuitem, *label;
+
+		if (act->children == NULL) {
+			menuitem = gtk_menu_item_new();
+			label = gtk_label_new("");
+			gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+			gtk_label_set_pattern(GTK_LABEL(label), "_");
+			gtk_label_set_markup_with_mnemonic(GTK_LABEL(label),
+			                                   act->label);
+			gtk_container_add(GTK_CONTAINER(menuitem), label);
+
+			if (act->callback != NULL) {
+				g_object_set_data(G_OBJECT(menuitem),
+				                  "gaimcallback",
+				                  act->callback);
+				g_object_set_data(G_OBJECT(menuitem),
+				                  "gaimcallbackdata",
+				                  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);
+		} else {
+			GList *l = NULL;
+			GtkWidget *submenu = NULL;
+
+			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);
+
+			for (l = act->children; l; l = l->next) {
+				GaimMenuAction *act = (GaimMenuAction *)l->data;
+
+				gaim_gtk_append_menu_action(submenu, act, object);
+			}
+			g_list_free(act->children);
+			act->children = NULL;
+		}
+		g_free(act);
+	}
+}
--- a/src/gtkutils.h	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/gtkutils.h	Tue Jan 17 23:22:19 2006 +0000
@@ -28,6 +28,7 @@
 #include "gtkconv.h"
 #include "gtkgaim.h"
 #include "prpl.h"
+#include "util.h"
 
 typedef enum
 {
@@ -370,4 +371,16 @@
  */
 GdkPixbuf * gaim_gtk_create_prpl_icon_with_status(GaimAccount *account, GaimStatusType *status_type);
 
+/**
+ * Append a GaimMenuAction to a menu.
+ *
+ * @param menu   The menu to append to.
+ * @param act    The GaimMenuAction to append.
+ * @param object The object to be passed to the action callback.
+ *
+ * @return The menu.
+ */
+void gaim_gtk_append_menu_action(GtkWidget *menu, GaimMenuAction *act,
+                                 gpointer gobject);
+
 #endif /* _GAIM_GTKUTILS_H_ */
--- a/src/protocols/gg/gg.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/protocols/gg/gg.c	Tue Jan 17 23:22:19 2006 +0000
@@ -1463,25 +1463,28 @@
 /* static GList *ggp_blist_node_menu(GaimBlistNode *node) {{{ */
 static GList *ggp_blist_node_menu(GaimBlistNode *node)
 {
-	GaimBlistNodeAction *act;
+	GaimMenuAction *act;
 	GList *m = NULL;
 
 	if (!GAIM_BLIST_NODE_IS_BUDDY(node))
 		return NULL;
 
-	act = gaim_blist_node_action_new(_("Add to chat"),
-					 ggp_bmenu_add_to_chat, NULL, NULL);
+	act = gaim_menu_action_new(_("Add to chat"),
+	                           GAIM_CALLBACK(ggp_bmenu_add_to_chat),
+	                           NULL, NULL);
 	m = g_list_append(m, act);
 
 	/* Using a blist node boolean here is also wrong.
 	 * Once the Block and Unblock actions are added to the core,
 	 * this will have to go. -- rlaager */
 	if (gaim_blist_node_get_bool(node, "blocked")) {
-		act = gaim_blist_node_action_new(_("Unblock"),
-						 ggp_bmenu_block, NULL, NULL);
+		act = gaim_menu_action_new(_("Unblock"),
+		                           GAIM_CALLBACK(ggp_bmenu_block),
+		                           NULL, NULL);
 	} else {
-		act = gaim_blist_node_action_new(_("Block"),
-						 ggp_bmenu_block, NULL, NULL);
+		act = gaim_menu_action_new(_("Block"),
+		                           GAIM_CALLBACK(ggp_bmenu_block),
+		                           NULL, NULL);
 	}
 	m = g_list_append(m, act);
 
--- a/src/protocols/jabber/buddy.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/protocols/jabber/buddy.c	Tue Jan 17 23:22:19 2006 +0000
@@ -1020,7 +1020,7 @@
 	JabberBuddy *jb = jabber_buddy_find(js, buddy->name, TRUE);
 
 	GList *m = NULL;
-	GaimBlistNodeAction *act;
+	GaimMenuAction *act;
 
 	if(!jb)
 		return m;
@@ -1029,32 +1029,37 @@
 
 	if(js->protocol_version == JABBER_PROTO_0_9 /* && NOT ME */) {
 		if(jb->invisible & JABBER_INVIS_BUDDY) {
-			act = gaim_blist_node_action_new(_("Un-hide From"),
-					jabber_buddy_make_visible, NULL, NULL);
+			act = gaim_menu_action_new(_("Un-hide From"),
+			                           GAIM_CALLBACK(jabber_buddy_make_visible),
+			                           NULL, NULL);
 		} else {
-			act = gaim_blist_node_action_new(_("Temporarily Hide From"),
-					jabber_buddy_make_invisible, NULL, NULL);
+			act = gaim_menu_action_new(_("Temporarily Hide From"),
+			                GAIM_CALLBACK(jabber_buddy_make_invisible),
+			                NULL, NULL);
 		}
 		m = g_list_append(m, act);
 	}
 
 	if(jb->subscription & JABBER_SUB_FROM /* && NOT ME */) {
-		act = gaim_blist_node_action_new(_("Cancel Presence Notification"),
-				jabber_buddy_cancel_presence_notification, NULL, NULL);
+		act = gaim_menu_action_new(_("Cancel Presence Notification"),
+		                           GAIM_CALLBACK(jabber_buddy_cancel_presence_notification),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
 	if(!(jb->subscription & JABBER_SUB_TO)) {
-		act = gaim_blist_node_action_new(_("(Re-)Request authorization"),
-				jabber_buddy_rerequest_auth, NULL, NULL);
+		act = gaim_menu_action_new(_("(Re-)Request authorization"),
+		                           GAIM_CALLBACK(jabber_buddy_rerequest_auth),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 
 	} else /* if(NOT ME) */{
 
 		/* shouldn't this just happen automatically when the buddy is
 		   removed? */
-		act = gaim_blist_node_action_new(_("Unsubscribe"),
-				jabber_buddy_unsubscribe, NULL, NULL);
+		act = gaim_menu_action_new(_("Unsubscribe"),
+		                           GAIM_CALLBACK(jabber_buddy_unsubscribe),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
--- a/src/protocols/msn/msn.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/protocols/msn/msn.c	Tue Jan 17 23:22:19 2006 +0000
@@ -666,7 +666,7 @@
 	MsnUser *user;
 
 	GList *m = NULL;
-	GaimBlistNodeAction *act;
+	GaimMenuAction *act;
 
 	g_return_val_if_fail(buddy != NULL, NULL);
 
@@ -676,8 +676,9 @@
 	{
 		if (user->mobile)
 		{
-			act = gaim_blist_node_action_new(_("Send to Mobile"),
-											 show_send_to_mobile_cb, NULL, NULL);
+			act = gaim_menu_action_new(_("Send to Mobile"),
+			                           GAIM_CALLBACK(show_send_to_mobile_cb),
+			                           NULL, NULL);
 			m = g_list_append(m, act);
 		}
 	}
@@ -685,8 +686,9 @@
 	if (g_ascii_strcasecmp(buddy->name,
 						   gaim_account_get_username(buddy->account)))
 	{
-		act = gaim_blist_node_action_new(_("Initiate _Chat"),
-										 initiate_chat_cb, NULL, NULL);
+		act = gaim_menu_action_new(_("Initiate _Chat"),
+		                           GAIM_CALLBACK(initiate_chat_cb),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
--- a/src/protocols/novell/novell.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/protocols/novell/novell.c	Tue Jan 17 23:22:19 2006 +0000
@@ -3438,11 +3438,11 @@
 novell_blist_node_menu(GaimBlistNode *node)
 {
 	GList *list = NULL;
-	GaimBlistNodeAction *act;
+	GaimMenuAction *act;
 
 	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
-		act = gaim_blist_node_action_new(_("Initiate _Chat"),
-		                                _initiate_conference_cb, NULL, NULL);
+		act = gaim_menu_action_new(_("Initiate _Chat"),
+		                           _initiate_conference_cb, NULL, NULL);
 		list = g_list_append(list, act);
 	}
 
--- a/src/protocols/oscar/oscar.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/protocols/oscar/oscar.c	Tue Jan 17 23:22:19 2006 +0000
@@ -8094,16 +8094,18 @@
 	OscarData *od = gc->proto_data;
 
 	GList *m = NULL;
-	GaimBlistNodeAction *act;
-
-	act = gaim_blist_node_action_new(_("Edit Buddy Comment"),
-			oscar_buddycb_edit_comment, NULL, NULL);
+	GaimMenuAction *act;
+
+	act = gaim_menu_action_new(_("Edit Buddy Comment"),
+	                           GAIM_CALLBACK(oscar_buddycb_edit_comment),
+	                           NULL, NULL);
 	m = g_list_append(m, act);
 
 	if (od->icq) {
 #if 0
-		act = gaim_blist_node_action_new(_("Get Status Msg"),
-				oscar_get_icqstatusmsg, NULL, NULL);
+		act = gaim_menu_action_new(_("Get Status Msg"),
+		                           GAIM_CALLBACK(oscar_get_icqstatusmsg),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 #endif
 	} else {
@@ -8114,14 +8116,16 @@
 				GAIM_BUDDY_IS_ONLINE(buddy)) {
 
 			if (userinfo->capabilities & AIM_CAPS_DIRECTIM) {
-				act = gaim_blist_node_action_new(_("Direct IM"),
-						oscar_ask_direct_im, NULL, NULL);
+				act = gaim_menu_action_new(_("Direct IM"),
+				                           GAIM_CALLBACK(oscar_ask_direct_im),
+				                           NULL, NULL);
 				m = g_list_append(m, act);
 			}
 #if 0
 			if (userinfo->capabilities & AIM_CAPS_GETFILE) {
-				act = gaim_blist_node_action_new(_("Get File"),
-						oscar_ask_getfile, NULL, NULL);
+				act = gaim_menu_action_new(_("Get File"),
+				                           GAIM_CALLBACK(oscar_ask_getfile),
+				                           NULL, NULL);
 				m = g_list_append(m, act);
 			}
 #endif
@@ -8131,8 +8135,9 @@
 	if (od->sess->ssi.received_data) {
 		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, NULL);
+			act = gaim_menu_action_new(_("Re-request Authorization"),
+			                           GAIM_CALLBACK(gaim_auth_sendrequest_menu),
+			                           NULL, NULL);
 			m = g_list_append(m, act);
 		}
 	}
--- a/src/protocols/sametime/sametime.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/protocols/sametime/sametime.c	Tue Jan 17 23:22:19 2006 +0000
@@ -1222,7 +1222,7 @@
   const char *owner;
   GaimGroup *group;
   GaimAccount *acct;
-  GaimBlistNodeAction *act;
+  GaimMenuAction *act;
 
   /* we only want groups */
   if(! GAIM_BLIST_NODE_IS_GROUP(node)) return;
@@ -1238,8 +1238,8 @@
   /* if there's anyone in the group for this acct, offer to invite
      them all to a conference */
   if(gaim_group_on_account(group, acct)) {
-    act = gaim_blist_node_action_new(_("Invite Group to Conference..."),
-				     blist_menu_group_invite, pd, NULL);
+    act = gaim_menu_action_new(_("Invite Group to Conference..."),
+                               blist_menu_group_invite, pd, NULL);
     *menu = g_list_append(*menu, NULL);
   }
 #endif
@@ -1247,8 +1247,8 @@
   /* check if it's a NAB group for this account */
   owner = gaim_blist_node_get_string(node, GROUP_KEY_OWNER);
   if(owner && !strcmp(owner, gaim_account_get_username(acct))) {
-    act = gaim_blist_node_action_new(_("Get Notes Address Book Info"),
-				     blist_menu_nab, pd, NULL);
+    act = gaim_menu_action_new(_("Get Notes Address Book Info"),
+                               blist_menu_nab, pd, NULL);
     *menu = g_list_append(*menu, act);
   }
 }
@@ -3484,15 +3484,15 @@
 
 static GList *mw_prpl_blist_node_menu(GaimBlistNode *node) {
   GList *l = NULL;
-  GaimBlistNodeAction *act;
+  GaimMenuAction *act;
 
   if(! GAIM_BLIST_NODE_IS_BUDDY(node))
     return l;
 
   l = g_list_append(l, NULL);
 
-  act = gaim_blist_node_action_new(_("Invite to Conference..."),
-				   blist_menu_conf, NULL, NULL);
+  act = gaim_menu_action_new(_("Invite to Conference..."),
+                             blist_menu_conf, NULL, NULL);
   l = g_list_append(l, act);
 
   /** note: this never gets called for a GaimGroup, have to use the
--- a/src/protocols/silc/buddy.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/protocols/silc/buddy.c	Tue Jan 17 23:22:19 2006 +0000
@@ -1619,7 +1619,7 @@
 	SilcClientConnection conn = sg->conn;
 	const char *pkfile = NULL;
 	SilcClientEntry client_entry = NULL;
-	GaimBlistNodeAction *act;
+	GaimMenuAction *act;
 	GList *m = NULL;
 	SilcGaimBuddyWb wb;
 
@@ -1629,36 +1629,40 @@
 						    buddy->proto_data);
 
 	if (client_entry && client_entry->send_key) {
-		act = gaim_blist_node_action_new(_("Reset IM Key"),
-		                                 silcgaim_buddy_resetkey, NULL, NULL);
+		act = gaim_menu_action_new(_("Reset IM Key"),
+		                           GAIM_CALLBACK(silcgaim_buddy_resetkey),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 
 	} else {
-		act = gaim_blist_node_action_new(_("IM with Key Exchange"),
-		                                 silcgaim_buddy_keyagr, NULL, NULL);
+		act = gaim_menu_action_new(_("IM with Key Exchange"),
+		                           GAIM_CALLBACK(silcgaim_buddy_keyagr),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 
-		act = gaim_blist_node_action_new(_("IM with Password"),
-		                                 silcgaim_buddy_privkey_menu,
-		                                 NULL, NULL);
+		act = gaim_menu_action_new(_("IM with Password"),
+		                           GAIM_CALLBACK(silcgaim_buddy_privkey_menu),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
 	if (pkfile) {
-		act = gaim_blist_node_action_new(_("Show Public Key"),
-		                                 silcgaim_buddy_showkey, NULL, NULL);
+		act = gaim_menu_action_new(_("Show Public Key"),
+		                           GAIM_CALLBACK(silcgaim_buddy_showkey),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 
 	} else {
-		act = gaim_blist_node_action_new(_("Get Public Key..."),
-		                                 silcgaim_buddy_getkey_menu,
-		                                 NULL, NULL);
+		act = gaim_menu_action_new(_("Get Public Key..."),
+		                           GAIM_CALLBACK(silcgaim_buddy_getkey_menu),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
 	if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) {
-		act = gaim_blist_node_action_new(_("Kill User"),
-		                                 silcgaim_buddy_kill, NULL, NULL);
+		act = gaim_menu_action_new(_("Kill User"),
+		                           GAIM_CALLBACK(silcgaim_buddy_kill),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
@@ -1666,8 +1670,9 @@
 		wb = silc_calloc(1, sizeof(*wb));
 		wb->sg = sg;
 		wb->client_entry = client_entry;
-		act = gaim_blist_node_action_new(_("Draw On Whiteboard"),
-		                                 silcgaim_buddy_wb, (void *)wb, NULL);
+		act = gaim_menu_action_new(_("Draw On Whiteboard"),
+		                           GAIM_CALLBACK(silcgaim_buddy_wb),
+		                           (void *)wb, NULL);
 		m = g_list_append(m, act);
 	}
 	return m;
--- a/src/protocols/silc/chat.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/protocols/silc/chat.c	Tue Jan 17 23:22:19 2006 +0000
@@ -858,7 +858,7 @@
 	SilcUInt32 mode = 0;
 
 	GList *m = NULL;
-	GaimBlistNodeAction *act;
+	GaimMenuAction *act;
 
 	if (components)
 		chname = g_hash_table_lookup(components, "channel");
@@ -874,103 +874,104 @@
 	if (strstr(chname, "[Private Group]"))
 		return NULL;
 
-	act = gaim_blist_node_action_new(_("Get Info"),
-	                                 silcgaim_chat_getinfo_menu,
-	                                 NULL, NULL);
+	act = gaim_menu_action_new(_("Get Info"),
+	                           GAIM_CALLBACK(silcgaim_chat_getinfo_menu),
+	                           NULL, 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) {
-		act = gaim_blist_node_action_new(_("Invite List"),
-		                                 silcgaim_chat_invitelist,
-		                                 NULL, NULL);
+		act = gaim_menu_action_new(_("Invite List"),
+		                           GAIM_CALLBACK(silcgaim_chat_invitelist),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 
-		act = gaim_blist_node_action_new(_("Ban List"),
-		                                 silcgaim_chat_banlist,
-		                                 NULL, NULL);
+		act = gaim_menu_action_new(_("Ban List"),
+		                           GAIM_CALLBACK(silcgaim_chat_banlist),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 #endif
 
 	if (chu) {
-		act = gaim_blist_node_action_new(_("Add Private Group"),
-		                                 silcgaim_chat_prv,
-		                                 NULL, NULL);
+		act = gaim_menu_action_new(_("Add Private Group"),
+		                           GAIM_CALLBACK(silcgaim_chat_prv),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
 	if (mode & SILC_CHANNEL_UMODE_CHANFO) {
-		act = gaim_blist_node_action_new(_("Channel Authentication"),
-		                                 silcgaim_chat_chauth,
-		                                 NULL, NULL);
+		act = gaim_menu_action_new(_("Channel Authentication"),
+		                           GAIM_CALLBACK(silcgaim_chat_chauth),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 
 		if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
-			act = gaim_blist_node_action_new(_("Reset Permanent"),
-			                                 silcgaim_chat_permanent_reset,
-			                                 NULL, NULL);
+			act = gaim_menu_action_new(_("Reset Permanent"),
+			                           GAIM_CALLBACK(silcgaim_chat_permanent_reset),
+			                           NULL, NULL);
 			m = g_list_append(m, act);
 		} else {
-			act = gaim_blist_node_action_new(_("Set Permanent"),
-			                                 silcgaim_chat_permanent,
-			                                 NULL, NULL);
+			act = gaim_menu_action_new(_("Set Permanent"),
+			                           GAIM_CALLBACK(silcgaim_chat_permanent),
+			                           NULL, NULL);
 			m = g_list_append(m, act);
 		}
 	}
 
 	if (mode & SILC_CHANNEL_UMODE_CHANOP) {
-		act = gaim_blist_node_action_new(_("Set User Limit"),
-		                                 silcgaim_chat_ulimit,
-		                                 NULL, NULL);
+		act = gaim_menu_action_new(_("Set User Limit"),
+		                           GAIM_CALLBACK(silcgaim_chat_ulimit),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 
 		if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
-			act = gaim_blist_node_action_new(_("Reset Topic Restriction"),
-			                                 silcgaim_chat_resettopic,
-			                                 NULL, NULL);
+			act = gaim_menu_action_new(_("Reset Topic Restriction"),
+			                           GAIM_CALLBACK(silcgaim_chat_resettopic),
+			                           NULL, NULL);
 			m = g_list_append(m, act);
 		} else {
-			act = gaim_blist_node_action_new(_("Set Topic Restriction"),
-			                                 silcgaim_chat_settopic,
-			                                 NULL, NULL);
+			act = gaim_menu_action_new(_("Set Topic Restriction"),
+			                           GAIM_CALLBACK(silcgaim_chat_settopic),
+			                           NULL, NULL);
 			m = g_list_append(m, act);
 		}
 
 		if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) {
-			act = gaim_blist_node_action_new(_("Reset Private Channel"),
-			                                 silcgaim_chat_resetprivate,
-			                                 NULL, NULL);
+			act = gaim_menu_action_new(_("Reset Private Channel"),
+			                           GAIM_CALLBACK(silcgaim_chat_resetprivate),
+			                           NULL, NULL);
 			m = g_list_append(m, act);
 		} else {
-			act = gaim_blist_node_action_new(_("Set Private Channel"),
-			                                 silcgaim_chat_setprivate,
-			                                 NULL, NULL);
+			act = gaim_menu_action_new(_("Set Private Channel"),
+			                           GAIM_CALLBACK(silcgaim_chat_setprivate),
+			                           NULL, NULL);
 			m = g_list_append(m, act);
 		}
 
 		if (channel->mode & SILC_CHANNEL_MODE_SECRET) {
-			act = gaim_blist_node_action_new(_("Reset Secret Channel"),
-			                                 silcgaim_chat_resetsecret,
-			                                 NULL, NULL);
+			act = gaim_menu_action_new(_("Reset Secret Channel"),
+			                           GAIM_CALLBACK(silcgaim_chat_resetsecret),
+			                           NULL, NULL);
 			m = g_list_append(m, act);
 		} else {
-			act = gaim_blist_node_action_new(_("Set Secret Channel"),
-			                                 silcgaim_chat_setsecret,
-			                                 NULL, NULL);
+			act = gaim_menu_action_new(_("Set Secret Channel"),
+			                           GAIM_CALLBACK(silcgaim_chat_setsecret),
+			                           NULL, NULL);
 			m = g_list_append(m, act);
 		}
 	}
 
 	if (channel) {
 		SilcGaimChatWb wb;
-	        wb = silc_calloc(1, sizeof(*wb));
-        	wb->sg = sg;
-        	wb->channel = channel;
-        	act = gaim_blist_node_action_new(_("Draw On Whiteboard"),
-                                         silcgaim_chat_wb, (void *)wb, NULL);
-	        m = g_list_append(m, act);
+		wb = silc_calloc(1, sizeof(*wb));
+		wb->sg = sg;
+		wb->channel = channel;
+		act = gaim_menu_action_new(_("Draw On Whiteboard"),
+		                           GAIM_CALLBACK(silcgaim_chat_wb),
+		                           (void *)wb, NULL);
+		m = g_list_append(m, act);
 	}
 
 	return m;
--- a/src/protocols/toc/toc.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/protocols/toc/toc.c	Tue Jan 17 23:22:19 2006 +0000
@@ -1543,11 +1543,11 @@
 static GList *toc_blist_node_menu(GaimBlistNode *node)
 {
 	GList *m = NULL;
-	GaimBlistNodeAction *act;
+	GaimMenuAction *act;
 
 	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
-		act = gaim_blist_node_action_new(_("Get Dir Info"),
-		                                 toc_get_dir, NULL, NULL);
+		act = gaim_menu_action_new(_("Get Dir Info"),
+		                           toc_get_dir, NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
--- a/src/protocols/yahoo/yahoo.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/protocols/yahoo/yahoo.c	Tue Jan 17 23:22:19 2006 +0000
@@ -2858,19 +2858,19 @@
 
 static GList *build_presence_submenu(YahooFriend *f, GaimConnection *gc) {
 	GList *m = NULL;
-	GaimBlistNodeAction *act;
+	GaimMenuAction *act;
 	struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data;
 
 	if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
 		if (f->presence != YAHOO_PRESENCE_ONLINE) {
-			act = gaim_blist_node_action_new(_("Appear Online"),
-					yahoo_presence_settings,
+			act = gaim_menu_action_new(_("Appear Online"),
+					GAIM_CALLBACK(yahoo_presence_settings),
 					GINT_TO_POINTER(YAHOO_PRESENCE_ONLINE),
 					NULL);
 			m = g_list_append(m, act);
 		} else if (f->presence != YAHOO_PRESENCE_DEFAULT) {
-			act = gaim_blist_node_action_new(_("Appear Offline"),
-					yahoo_presence_settings,
+			act = gaim_menu_action_new(_("Appear Offline"),
+					GAIM_CALLBACK(yahoo_presence_settings),
 					GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT),
 					NULL);
 			m = g_list_append(m, act);
@@ -2878,15 +2878,14 @@
 	}
 
 	if (f->presence == YAHOO_PRESENCE_PERM_OFFLINE) {
-		act = gaim_blist_node_action_new(
+		act = gaim_menu_action_new(
 				_("Don't Appear Permanently Offline"),
-				yahoo_presence_settings,
+				GAIM_CALLBACK(yahoo_presence_settings),
 				GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT), NULL);
 		m = g_list_append(m, act);
 	} else {
-		act = gaim_blist_node_action_new(
-				_("Appear Permanently Offline"),
-				yahoo_presence_settings,
+		act = gaim_menu_action_new(_("Appear Permanently Offline"),
+				GAIM_CALLBACK(yahoo_presence_settings),
 				GINT_TO_POINTER(YAHOO_PRESENCE_PERM_OFFLINE),
 				NULL);
 		m = g_list_append(m, act);
@@ -2906,7 +2905,7 @@
 static GList *yahoo_buddy_menu(GaimBuddy *buddy)
 {
 	GList *m = NULL;
-	GaimBlistNodeAction *act;
+	GaimMenuAction *act;
 
 	GaimConnection *gc = gaim_account_get_connection(buddy->account);
 	struct yahoo_data *yd = gc->proto_data;
@@ -2916,8 +2915,9 @@
 	f = yahoo_friend_find(gc, buddy->name);
 
 	if (!f && !yd->wm) {
-		act = gaim_blist_node_action_new(_("Add Buddy"),
-				yahoo_addbuddyfrommenu_cb, NULL, NULL);
+		act = gaim_menu_action_new(_("Add Buddy"),
+		                           GAIM_CALLBACK(yahoo_addbuddyfrommenu_cb),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 
 		return m;
@@ -2926,13 +2926,15 @@
 
 	if (f && f->status != YAHOO_STATUS_OFFLINE) {
 		if (!yd->wm) {
-			act = gaim_blist_node_action_new(_("Join in Chat"),
-				yahoo_chat_goto_menu, NULL, NULL);
+			act = gaim_menu_action_new(_("Join in Chat"),
+			                           GAIM_CALLBACK(yahoo_chat_goto_menu),
+			                           NULL, NULL);
 			m = g_list_append(m, act);
 		}
 
-		act = gaim_blist_node_action_new(_("Initiate Conference"),
-			yahoo_initiate_conference, NULL, NULL);
+		act = gaim_menu_action_new(_("Initiate Conference"),
+		                           GAIM_CALLBACK(yahoo_initiate_conference),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 
 		if (yahoo_friend_get_game(f)) {
@@ -2949,21 +2951,24 @@
 				*t = ' ';
 				g_snprintf(buf2, sizeof buf2, "%s", room);
 
-				act = gaim_blist_node_action_new(buf2, yahoo_game, NULL, NULL);
+				act = gaim_menu_action_new(buf2,
+				                           GAIM_CALLBACK(yahoo_game),
+				                           NULL, NULL);
 				m = g_list_append(m, act);
 			}
 		}
 	}
 
 	if (f) {
-		act = gaim_blist_node_action_new(_("Presence Settings"),
-				NULL, NULL, build_presence_submenu(f, gc));
+		act = gaim_menu_action_new(_("Presence Settings"), NULL, NULL,
+		                           build_presence_submenu(f, gc));
 		m = g_list_append(m, act);
 	}
 
 	if (f) {
-		act = gaim_blist_node_action_new(_("Start Doodling"),
-				yahoo_doodle_blist_node, NULL, NULL);
+		act = gaim_menu_action_new(_("Start Doodling"),
+		                           GAIM_CALLBACK(yahoo_doodle_blist_node),
+		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
--- a/src/util.c	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/util.c	Tue Jan 17 23:22:19 2006 +0000
@@ -65,6 +65,18 @@
 static char custom_home_dir[MAXPATHLEN];
 static char home_dir[MAXPATHLEN];
 
+GaimMenuAction *
+gaim_menu_action_new(char *label, GaimCallback callback, gpointer data,
+                     GList *children)
+{
+	GaimMenuAction *act = g_new0(GaimMenuAction, 1);
+	act->label = label;
+	act->callback = callback;
+	act->data = data;
+	act->children = children;
+	return act;
+}
+
 /**************************************************************************
  * Base16 Functions
  **************************************************************************/
--- a/src/util.h	Tue Jan 17 19:28:45 2006 +0000
+++ b/src/util.h	Tue Jan 17 23:22:19 2006 +0000
@@ -37,6 +37,13 @@
 extern "C" {
 #endif
 
+typedef struct _GaimMenuAction
+{
+	char *label;
+	GaimCallback callback;
+	gpointer data;
+	GList *children;
+} GaimMenuAction;
 
 /**
  * A key-value pair.
@@ -51,7 +58,18 @@
 
 } GaimKeyValuePair;
 
-
+/**
+ * Creates a new GaimMenuAction.
+ * @param label    The text label to display for this action.
+ * @param callback The function to be called when the action is used on
+ *                 the selected item.
+ * @param data     Additional data to be passed to the callback.
+ * @param children A GList of GaimMenuActions to be added as a submenu
+ *                 of the action.
+ * @return The GaimMenuAction.
+ */
+GaimMenuAction *gaim_menu_action_new(char *label, GaimCallback callback,
+                                     gpointer data, GList *children);
 
 /**************************************************************************/
 /** @name Base16 Functions                                                */
@@ -90,7 +108,6 @@
 
 /*@}*/
 
-
 /**************************************************************************/
 /** @name Base64 Functions                                                */
 /**************************************************************************/