# HG changeset patch
# Node ID 74d307ce7c7e
# Parent  bfc62c3fe310
diff --git a/libpurple/purplecontactmanager.c b/libpurple/purplecontactmanager.c
--- a/libpurple/purplecontactmanager.c
+++ b/libpurple/purplecontactmanager.c
@@ -18,6 +18,8 @@
 
 #include <glib/gi18n-lib.h>
 
+#include <birb.h>
+
 #include "purplecontactmanager.h"
 
 #include "purplegdkpixbuf.h"
@@ -30,6 +32,7 @@
 	SIG_REMOVED,
 	SIG_PERSON_ADDED,
 	SIG_PERSON_REMOVED,
+	SIG_POPULATE_MENU,
 	N_SIGNALS,
 };
 static guint signals[N_SIGNALS] = {0, };
@@ -191,6 +194,14 @@
 }
 
 static void
+purple_contact_manager_contact_populate_menu_cb(PurpleContactInfo *info,
+                                                GMenu *menu,
+                                                gpointer data)
+{
+	g_signal_emit(data, signals[SIG_POPULATE_MENU], 0, info, menu);
+}
+
+static void
 purple_contact_manager_tags_changed_cb(G_GNUC_UNUSED PurpleTags *tags,
                                        G_GNUC_UNUSED const char *tag,
                                        G_GNUC_UNUSED const char *name,
@@ -374,6 +385,31 @@
 		G_TYPE_NONE,
 		1,
 		PURPLE_TYPE_PERSON);
+
+	/**
+	 * PurpleContactManager::populate-menu:
+	 * @manager: The instance.
+	 * @info: The [class@ContactInfo] instance.
+	 * @menu: The [class@Gio.Menu] to populate.
+	 *
+	 * This is a propagation of [signal@ContactInfo::populate-menu]. This means
+	 * that your callback will be called for any contact info to populate its
+	 * context menu.
+	 *
+	 * Since: 3.0.0
+	 */
+	signals[SIG_POPULATE_MENU] = g_signal_new_class_handler(
+		"populate-menu",
+		G_OBJECT_CLASS_TYPE(klass),
+		G_SIGNAL_RUN_LAST,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		2,
+		PURPLE_TYPE_CONTACT_INFO,
+		BIRB_TYPE_ACTION_MENU);
 }
 
 /******************************************************************************
@@ -455,15 +491,19 @@
 		tags = purple_contact_info_get_tags(info);
 
 		/* Add some notify signals to track changes. */
-		g_signal_connect_object(contact, "notify::alias",
+		g_signal_connect_object(info, "notify::alias",
+		                        G_CALLBACK(purple_contact_manager_contact_update_cb),
+		                        manager, 0);
+		g_signal_connect_object(info, "notify::permission",
 		                        G_CALLBACK(purple_contact_manager_contact_update_cb),
 		                        manager, 0);
-		g_signal_connect_object(contact, "notify::permission",
-		                        G_CALLBACK(purple_contact_manager_contact_update_cb),
-		                        manager, 0);
-		g_signal_connect_object(contact, "notify::person",
+		g_signal_connect_object(info, "notify::person",
 		                        G_CALLBACK(purple_contact_manager_contact_person_changed_cb),
 		                        manager, 0);
+		g_signal_connect_object(info, "populate-menu",
+		                        G_CALLBACK(purple_contact_manager_contact_populate_menu_cb),
+		                        manager, 0);
+
 		g_signal_connect_object(tags, "added",
 		                        G_CALLBACK(purple_contact_manager_tags_changed_cb),
 		                        contact, 0);
diff --git a/libpurple/tests/test_contact_manager.c b/libpurple/tests/test_contact_manager.c
--- a/libpurple/tests/test_contact_manager.c
+++ b/libpurple/tests/test_contact_manager.c
@@ -20,6 +20,8 @@
 
 #include <purple.h>
 
+#include <birb.h>
+
 #include "test_ui.h"
 
 /******************************************************************************
@@ -466,6 +468,64 @@
 }
 
 /******************************************************************************
+ * propagate_populate_menu tests
+ *****************************************************************************/
+static void
+test_purple_contact_manager_propagate_populate_menu_cb(PurpleContactManager *manager,
+                                                       PurpleContactInfo *info,
+                                                       BirbActionMenu *action_menu,
+                                                       gpointer data)
+{
+	GMenu *menu = NULL;
+	guint *counter = data;
+
+	g_assert_true(PURPLE_IS_CONTACT_MANAGER(manager));
+	g_assert_true(PURPLE_IS_CONTACT_INFO(info));
+	g_assert_true(BIRB_IS_ACTION_MENU(action_menu));
+
+	menu = birb_action_menu_get_menu(action_menu);
+	g_menu_append(menu, "test", "test.action");
+
+	/* Increment our counter. */
+	*counter = *counter + 1;
+}
+
+static void
+test_purple_contact_manager_propagate_populate_menu(void) {
+	PurpleAccount *account = NULL;
+	PurpleContactManager *manager = NULL;
+	PurpleContact *contact = NULL;
+	BirbActionMenu *action_menu = NULL;
+	GMenu *menu = NULL;
+	guint counter = 0;
+
+	account = purple_account_new("test", "test");
+
+	manager = g_object_new(PURPLE_TYPE_CONTACT_MANAGER, NULL);
+	g_signal_connect(manager, "populate-menu",
+	                 G_CALLBACK(test_purple_contact_manager_propagate_populate_menu_cb),
+	                 &counter);
+
+	contact = purple_contact_new(account, NULL);
+	purple_contact_manager_add(manager, contact);
+
+	action_menu = purple_contact_info_get_menu(PURPLE_CONTACT_INFO(contact));
+	g_assert_true(BIRB_IS_ACTION_MENU(action_menu));
+
+	menu = birb_action_menu_get_menu(action_menu);
+	g_assert_true(G_IS_MENU(menu));
+
+	g_assert_cmpuint(counter, ==, 1);
+	g_assert_cmpuint(g_menu_model_get_n_items(G_MENU_MODEL(menu)), ==, 1);
+
+	g_assert_finalize_object(action_menu);
+
+	g_clear_object(&account);
+	g_clear_object(&manager);
+	g_clear_object(&contact);
+}
+
+/******************************************************************************
  * Main
  *****************************************************************************/
 gint
@@ -501,6 +561,9 @@
 	g_test_add_func("/contact-manager/person/add-via-contact-remove-person-with-contacts",
 	                test_purple_contact_manager_person_add_via_contact_remove_person_with_contacts);
 
+	g_test_add_func("/contact-manager/propagate-populate-menu",
+	                test_purple_contact_manager_propagate_populate_menu);
+
 	ret = g_test_run();
 
 	test_ui_purple_uninit();
