changeset 13958:6741419af7f7

[gaim-migrate @ 16510] Add a context menu for the buddy-list. Most of the menus don't do anything yet, because the request-ui is not done. But the others, eg. 'Get Info', 'Join In Chat' etc.) work. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Mon, 17 Jul 2006 21:23:38 +0000
parents 421259b9e06d
children 2ae35c0cf616
files console/gntblist.c
diffstat 1 files changed, 265 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/console/gntblist.c	Mon Jul 17 13:37:08 2006 +0000
+++ b/console/gntblist.c	Mon Jul 17 21:23:38 2006 +0000
@@ -1,5 +1,6 @@
 #include <account.h>
 #include <blist.h>
+#include <request.h>
 #include <server.h>
 #include <signal.h>
 #include <util.h>
@@ -19,6 +20,9 @@
 
 	GntWidget *tooltip;
 	GaimBlistNode *tnode;		/* Who is the tooltip being displayed for? */
+
+	GntWidget *context;
+	GaimBlistNode *cnode;
 } GGBlist;
 
 GGBlist *ggblist;
@@ -28,6 +32,7 @@
 static void add_chat(GaimChat *chat, GGBlist *ggblist);
 static void add_node(GaimBlistNode *node, GGBlist *ggblist);
 static void draw_tooltip(GGBlist *ggblist);
+static void remove_peripherals(GGBlist *ggblist);
 
 static void
 new_node(GaimBlistNode *node)
@@ -252,6 +257,231 @@
 }
 
 static void
+remove_context_menu(GGBlist *ggblist)
+{
+	if (ggblist->context)
+		gnt_widget_destroy(ggblist->context->parent);
+	ggblist->context = NULL;
+	ggblist->cnode = NULL;
+}
+
+static void
+gnt_append_menu_action(GntTree *tree, GaimMenuAction *action, gpointer parent)
+{
+	GList *list;
+	
+	gnt_tree_add_row_after(tree, action, action->label, parent, NULL);
+	for (list = action->children; list; list = list->next)
+		gnt_append_menu_action(tree, list->data, action);
+}
+
+static void
+append_proto_menu(GntTree *tree, GaimConnection *gc, GaimBlistNode *node)
+{
+	GList *list;
+	GaimPluginProtocolInfo *prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+	if(!prpl_info || !prpl_info->blist_node_menu)
+		return;
+
+	for(list = prpl_info->blist_node_menu(node); list;
+			list = g_list_delete_link(list, list))
+	{
+		GaimMenuAction *act = (GaimMenuAction *) list->data;
+		gnt_append_menu_action(tree, act, NULL);
+	}
+}
+
+static void
+add_custom_action(GntTree *tree, const char *label, GaimCallback callback,
+		gpointer data)
+{
+	GaimMenuAction *action = gaim_menu_action_new(label, callback, data, NULL);
+	gnt_append_menu_action(tree, action, NULL);
+	g_signal_connect_swapped(G_OBJECT(tree), "destroy",
+			G_CALLBACK(gaim_menu_action_free), action);
+}
+
+static void
+create_chat_menu(GntTree *tree, GaimChat *chat)
+{
+}
+
+static void
+create_group_menu(GntTree *tree, GaimGroup *group)
+{
+}
+
+static void
+gg_blist_get_buddy_info_cb(GaimBuddy *buddy, GaimBlistNode *null)
+{
+	serv_get_info(buddy->account->gc, gaim_buddy_get_name(buddy));
+}
+
+static void
+create_buddy_menu(GntTree *tree, GaimBuddy *buddy)
+{
+	GaimPluginProtocolInfo *prpl_info;
+
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(buddy->account->gc->prpl);
+	if (prpl_info && prpl_info->get_info)
+	{
+		add_custom_action(tree, _("Get Info"),
+				GAIM_CALLBACK(gg_blist_get_buddy_info_cb), buddy);
+	}
+
+#if 0
+	add_custom_action(tree, _("Add Buddy Pounce"),
+			GAIM_CALLBACK(gg_blist_add_buddy_pounce_cb)), buddy);
+
+	if (prpl_info && prpl_info->send_file)
+	{
+		if (!prpl_info->can_receive_file ||
+			prpl_info->can_receive_file(buddy->account->gc, buddy->name))
+			add_custom_action(tree, _("Send File"),
+					GAIM_CALLBACK(gg_blist_show_file_cb)), buddy);
+	}
+
+	add_custom_action(tree, _("View Log"),
+			GAIM_CALLBACK(gg_blist_view_log_cb)), buddy);
+#endif
+	
+	/* Protocol actions */
+	append_proto_menu(tree,
+			gaim_account_get_connection(gaim_buddy_get_account(buddy)),
+			(GaimBlistNode*)buddy);
+}
+
+static void
+append_extended_menu(GntTree *tree, GaimBlistNode *node)
+{
+	GList *iter;
+
+	for (iter = gaim_blist_node_get_extended_menu(node);
+			iter; iter = g_list_delete_link(iter, iter))
+	{
+		gnt_append_menu_action(tree, iter->data, NULL);
+	}
+}
+
+static void
+context_menu_callback(GntTree *tree, GGBlist *ggblist)
+{
+	GaimMenuAction *action = gnt_tree_get_selection_data(tree);
+	GaimBlistNode *node = ggblist->cnode;
+
+	if (action)
+	{
+		void (*callback)(GaimBlistNode *, gpointer);
+		callback = (void (*)(GaimBlistNode *, gpointer))action->callback;
+		callback(node, action->data);
+	}
+
+	remove_context_menu(ggblist);
+}
+
+static void
+gg_blist_rename_node_cb(GaimBlistNode *node, GaimBlistNode *null)
+{
+}
+
+/* XXX: This still doesn't do anything, because request doesn't have a ui yet */
+static void
+gg_blist_remove_node_cb(GaimBlistNode *node, GaimBlistNode *null)
+{
+	void (*callback)(gpointer);
+
+	if (GAIM_BLIST_NODE_IS_BUDDY(node))
+		callback = (void(*)(gpointer))gaim_blist_remove_buddy;
+	else if (GAIM_BLIST_NODE_IS_CHAT(node))
+		callback = (void(*)(gpointer))gaim_blist_remove_chat;
+	else if (GAIM_BLIST_NODE_IS_GROUP(node))
+		callback = (void(*)(gpointer))gaim_blist_remove_group;
+
+	/* XXX: anything to do with the returned ui-handle? */
+	gaim_request_action(node, _("Confirm Remove"),
+			_("Are you sure you want to remove ..."), NULL,    /* XXX: tidy up */
+			1, node, 2,
+			_("Remove"), callback,
+			_("No"), NULL);
+
+}
+
+static void
+draw_context_menu(GGBlist *ggblist)
+{
+	GaimBlistNode *node = NULL;
+	GntWidget *context = NULL, *window = NULL;
+	GntTree *tree = NULL;
+	int x, y, top, width;
+	char *title = NULL;
+
+	tree = GNT_TREE(ggblist->tree);
+
+	if (ggblist->context)
+	{
+		remove_context_menu(ggblist);
+	}
+
+	node = gnt_tree_get_selection_data(tree);
+
+	if (node == NULL)
+		return;
+	if (ggblist->tooltip)
+		remove_tooltip(ggblist);
+
+	ggblist->cnode = node;
+	ggblist->context = context = gnt_tree_new();
+	GNT_WIDGET_SET_FLAGS(context, GNT_WIDGET_NO_BORDER);
+	gnt_widget_set_name(context, "context menu");
+	g_signal_connect(G_OBJECT(context), "activate", G_CALLBACK(context_menu_callback), ggblist);
+
+	if (GAIM_BLIST_NODE_IS_BUDDY(node))
+	{
+		GaimBuddy *buddy = (GaimBuddy *)node;
+		create_buddy_menu(GNT_TREE(context), buddy);
+		title = g_strdup(gaim_buddy_get_name(buddy));
+	}
+	else if (GAIM_BLIST_NODE_IS_CHAT(node))
+	{
+		GaimChat *chat = (GaimChat*)node;
+		create_chat_menu(GNT_TREE(context), chat);
+		title = g_strdup(gaim_chat_get_name(chat));
+	}
+	else if (GAIM_BLIST_NODE_IS_GROUP(node))
+	{
+		GaimGroup *group = (GaimGroup *)node;
+		create_group_menu(GNT_TREE(context), group);
+		title = g_strdup(group->name);
+	}
+
+	append_extended_menu(GNT_TREE(context), node);
+
+	/* These are common for everything */
+	add_custom_action(GNT_TREE(context), _("Rename"),
+			GAIM_CALLBACK(gg_blist_rename_node_cb), node);
+	add_custom_action(GNT_TREE(context), _("Remove"),
+			GAIM_CALLBACK(gg_blist_remove_node_cb), node);
+
+	window = gnt_vbox_new(FALSE);
+	gnt_box_set_toplevel(GNT_BOX(window), TRUE);
+	gnt_box_set_title(GNT_BOX(window), title);
+			
+	gnt_box_add_widget(GNT_BOX(window), context);
+
+	/* Set the position for the popup */
+	gnt_widget_get_position(GNT_WIDGET(tree), &x, &y);
+	gnt_widget_get_size(GNT_WIDGET(tree), &width, NULL);
+	top = gnt_tree_get_selection_visible_line(tree);
+
+	x += width;
+	y += top - 1;
+
+	gnt_widget_set_position(window, x, y);
+	gnt_widget_draw(window);
+}
+
+static void
 draw_tooltip(GGBlist *ggblist)
 {
 	GaimBlistNode *node;
@@ -270,6 +500,9 @@
 	if (!gnt_widget_has_focus(ggblist->tree))
 		return;
 
+	if (ggblist->context)
+		return;
+
 	if (ggblist->tooltip)
 	{
 		/* XXX: Once we can properly redraw on expose events, this can be removed at the end
@@ -372,18 +605,35 @@
 static gboolean
 key_pressed(GntWidget *widget, const char *text, GGBlist *ggblist)
 {
+	gboolean stop = FALSE, ret = FALSE;
 	if (text[0] == 27 && text[1] == 0)
 	{
 		/* Escape was pressed */
-		if (ggblist->tooltip)
+		remove_peripherals(ggblist);
+		stop = TRUE;
+		ret = TRUE;
+	}
+
+	if (ggblist->context)
+	{
+		ret = gnt_widget_key_pressed(ggblist->context, text);
+		stop = TRUE;
+	}
+	
+	if (text[0] == 27)
+	{
+		if (strcmp(text + 1, GNT_KEY_POPUP) == 0)
 		{
-			gnt_widget_destroy(ggblist->tooltip);
-			ggblist->tooltip = NULL;
-			return TRUE;
+			draw_context_menu(ggblist);
+			stop = TRUE;
+			ret = TRUE;
 		}
 	}
 
-	return FALSE;
+	if (stop)
+		g_signal_stop_emission_by_name(G_OBJECT(widget), "key_pressed");
+
+	return ret;
 }
 
 static void
@@ -411,6 +661,15 @@
 	update_buddy_display(buddy, ggblist);
 }
 
+static void
+remove_peripherals(GGBlist *ggblist)
+{
+	if (ggblist->tooltip)
+		remove_tooltip(ggblist);
+	else if (ggblist->context)
+		remove_context_menu(ggblist);
+}
+
 void gg_blist_init()
 {
 	ggblist = g_new0(GGBlist, 1);
@@ -456,7 +715,7 @@
 	g_signal_connect(G_OBJECT(ggblist->tree), "activate", G_CALLBACK(selection_activate), ggblist);
 	g_signal_connect_data(G_OBJECT(ggblist->tree), "gained-focus", G_CALLBACK(draw_tooltip),
 				ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
-	g_signal_connect_data(G_OBJECT(ggblist->tree), "lost-focus", G_CALLBACK(remove_tooltip),
+	g_signal_connect_data(G_OBJECT(ggblist->tree), "lost-focus", G_CALLBACK(remove_peripherals),
 				ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
 }