# HG changeset patch
# Node ID 15fcb632d6db
# Parent  31e8c7c92e2f
diff --git a/doc/reference/finch/meson.build b/doc/reference/finch/meson.build
--- a/doc/reference/finch/meson.build
+++ b/doc/reference/finch/meson.build
@@ -21,6 +21,7 @@
 			'--output-dir=@OUTPUT@',
 			'--no-namespace-dir',
 			'--content-dir=@0@'.format(meson.current_source_dir()),
+			'--add-include-path=@0@'.format(meson.global_build_root() / 'subprojects/birb/birb'),
 			'--add-include-path=@0@'.format(meson.global_build_root() / 'subprojects/gplugin/gplugin'),
 			'--add-include-path=@0@'.format(meson.global_build_root() / 'subprojects/libgnt'),
 			'--add-include-path=@0@'.format(meson.project_build_root() / 'libpurple'),
diff --git a/doc/reference/libpurple/meson.build b/doc/reference/libpurple/meson.build
--- a/doc/reference/libpurple/meson.build
+++ b/doc/reference/libpurple/meson.build
@@ -35,6 +35,7 @@
 			'--output-dir=@OUTPUT@',
 			'--no-namespace-dir',
 			'--content-dir=@0@'.format(meson.current_source_dir()),
+			'--add-include-path=@0@'.format(meson.global_build_root() / 'subprojects/birb/birb'),
 			'--add-include-path=@0@'.format(meson.global_build_root() / 'subprojects/gplugin/gplugin'),
 			'@INPUT1@'
 		],
diff --git a/doc/reference/pidgin/meson.build b/doc/reference/pidgin/meson.build
--- a/doc/reference/pidgin/meson.build
+++ b/doc/reference/pidgin/meson.build
@@ -22,6 +22,7 @@
 			'--output-dir=@OUTPUT@',
 			'--no-namespace-dir',
 			'--content-dir=@0@'.format(meson.current_source_dir()),
+			'--add-include-path=@0@'.format(meson.global_build_root() / 'subprojects/birb/birb'),
 			'--add-include-path=@0@'.format(meson.global_build_root() / 'subprojects/gplugin/gplugin'),
 			'--add-include-path=@0@'.format(meson.global_build_root() / 'subprojects/gplugin/gplugin-gtk4'),
 			'--add-include-path=@0@'.format(meson.project_build_root() / 'libpurple'),
diff --git a/libpurple/meson.build b/libpurple/meson.build
--- a/libpurple/meson.build
+++ b/libpurple/meson.build
@@ -388,7 +388,7 @@
                     dependencies : # static_link_libs
                         [dnsapi, ws2_32, glib, gio, gplugin_dep, libsoup,
                          libxml, gdk_pixbuf, gstreamer, gstreamer_app, json,
-                         sqlite3, math])
+                         sqlite3, math, birb_dep])
 
 install_headers(purple_coreheaders,
                 subdir : purple_include_base)
@@ -418,14 +418,14 @@
 
 	libpurple_gir = gnome.generate_gir(libpurple,
 	    sources : introspection_sources,
-	    includes : ['GdkPixbuf-2.0', 'GLib-2.0', 'Gio-2.0', 'GObject-2.0', 'Gst-1.0', 'GPlugin-1.0'],
+	    includes : ['Birb-1.0', 'GdkPixbuf-2.0', 'GLib-2.0', 'Gio-2.0', 'GObject-2.0', 'Gst-1.0', 'GPlugin-1.0'],
 	    header : 'purple.h',
 	    namespace : 'Purple',
 	    symbol_prefix : 'purple',
 	    identifier_prefix : 'Purple',
 	    export_packages : purple_filebase,
 	    nsversion : f'@purple_major_version@.@purple_minor_version@',
-	    dependencies: [gplugin_dep],
+	    dependencies: [birb_dep, gplugin_dep],
 	    install : true,
 	    extra_args : ['-DPURPLE_COMPILATION', '--quiet'])
 
@@ -437,7 +437,7 @@
     sources : [purple_builtheaders] + purple_generated_sources,
     include_directories : [toplevel_inc, libpurple_inc],
     link_with : libpurple,
-    dependencies : [gdk_pixbuf, gstreamer, gplugin_dep, glib, gio])
+    dependencies : [birb_dep, gdk_pixbuf, gstreamer, gplugin_dep, glib, gio])
 
 meson.override_dependency(purple_filebase, libpurple_dep)
 
diff --git a/libpurple/protocols/ircv3/meson.build b/libpurple/protocols/ircv3/meson.build
--- a/libpurple/protocols/ircv3/meson.build
+++ b/libpurple/protocols/ircv3/meson.build
@@ -64,14 +64,14 @@
 ircv3_prpl = shared_library('ircv3', IRCV3_SOURCES + IRCV3_HEADERS + [ircv3_h],
 	c_args : ['-DPURPLE_IRCV3_COMPILATION', '-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Purple-IRCv3"'],
 	gnu_symbol_visibility : 'hidden',
-	dependencies : [libpurple_dep, birb, glib, gio, hasl],
+	dependencies : [birb_dep, libpurple_dep, glib, gio, hasl],
 	install : true,
 	install_dir : PURPLE_PLUGINDIR)
 
 ircv3_dep = declare_dependency(
 	sources : [IRCV3_SOURCES, IRCV3_HEADERS],
 	include_directories : ircv3_includes,
-	dependencies : [libpurple_dep, birb, glib, gio, hasl])
+	dependencies : [birb_dep, libpurple_dep, glib, gio, hasl])
 
 pkgconfig.generate(
 	# we purposely don't put the library here because you should not be
@@ -101,12 +101,12 @@
 	gnome.generate_gir(
 		ircv3_introspection_stub,
 		sources : [IRCV3_SOURCES, IRCV3_HEADERS],
-		includes : ['GLib-2.0', 'GObject-2.0', 'GPlugin-1.0', libpurple_gir[0]],
+		includes : ['Birb-1.0', 'GLib-2.0', 'GObject-2.0', 'GPlugin-1.0', libpurple_gir[0]],
 		namespace : 'PurpleIRCv3',
 		symbol_prefix : 'purple_ircv3',
 		nsversion : '1.0',
 		install : true,
-		dependencies: [gplugin_dep],
+		dependencies: [birb_dep, gplugin_dep],
 		export_packages : ['ircv3'],
 		extra_args : ['-DPURPLE_IRCV3_COMPILATION', '--verbose'])
 endif
diff --git a/libpurple/protocols/ircv3/tests/meson.build b/libpurple/protocols/ircv3/tests/meson.build
--- a/libpurple/protocols/ircv3/tests/meson.build
+++ b/libpurple/protocols/ircv3/tests/meson.build
@@ -7,7 +7,7 @@
 foreach prog : TESTS
 	e = executable(
 		f'test_ircv3_@prog@', f'test_ircv3_@prog@.c',
-		dependencies : [libpurple_dep, birb, glib, hasl],
+		dependencies : [birb_dep, libpurple_dep, glib, hasl],
 		objects : ircv3_prpl.extract_all_objects(),
 		c_args : ['-DPURPLE_IRCV3_COMPILATION'])
 
diff --git a/libpurple/purplecontactinfo.h b/libpurple/purplecontactinfo.h
--- a/libpurple/purplecontactinfo.h
+++ b/libpurple/purplecontactinfo.h
@@ -26,6 +26,8 @@
 #include <glib.h>
 #include <glib-object.h>
 
+#include <birb.h>
+
 #include "purpleavatar.h"
 #include "purplepresence.h"
 #include "purpletags.h"
@@ -525,6 +527,23 @@
 PURPLE_AVAILABLE_IN_3_0
 gboolean purple_contact_info_matches(PurpleContactInfo *info, const char *needle);
 
+/**
+ * purple_contact_info_get_menu:
+ * @info: The instance.
+ *
+ * Gets a [class@Birb.ActionMenu] that can be used as a context menu for @info.
+ *
+ * This will emit the [signal@ContactInfo::populate-menu] signals so that
+ * plugins can add additional items. Please note that the order of the menu is
+ * not guaranteed.
+ *
+ * Returns: (transfer full) (nullable): The menu for the contact info or %NULL.
+ *
+ * Since: 3.0.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+BirbActionMenu *purple_contact_info_get_menu(PurpleContactInfo *info);
+
 G_END_DECLS
 
 #endif /* PURPLE_CONTACT_INFO_H */
diff --git a/libpurple/purplecontactinfo.c b/libpurple/purplecontactinfo.c
--- a/libpurple/purplecontactinfo.c
+++ b/libpurple/purplecontactinfo.c
@@ -71,6 +71,7 @@
 
 enum {
 	SIG_PRESENCE_CHANGED,
+	SIG_POPULATE_MENU,
 	N_SIGNALS,
 };
 static guint signals[N_SIGNALS] = {0, };
@@ -623,6 +624,32 @@
 		2,
 		PURPLE_TYPE_PRESENCE,
 		G_TYPE_PARAM);
+
+	/**
+	 * PurpleContactInfo::populate-menu:
+	 * @info: The instance.
+	 * @menu: The [class@Birb.ActionMenu] to be displayed.
+	 *
+	 * Emitted in response to [method@PurpleContactInfo.get_menu] being called,
+	 * so that plugins can add additional items to @menu.
+	 *
+	 * The user interface is responsible for displaying @menu which means it
+	 * can add additional items, hide items, and lay them out however it
+	 * pleases.
+	 *
+	 * Since: 3.0.0
+	 */
+	signals[SIG_POPULATE_MENU] = g_signal_new_class_handler(
+		"populate-menu",
+		G_OBJECT_CLASS_TYPE(klass),
+		G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		1,
+		BIRB_TYPE_ACTION_MENU);
 }
 
 /******************************************************************************
@@ -1150,3 +1177,16 @@
 	/* Nothing matched, so return FALSE. */
 	return FALSE;
 }
+
+BirbActionMenu *
+purple_contact_info_get_menu(PurpleContactInfo *info) {
+	BirbActionMenu *menu = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_CONTACT_INFO(info), NULL);
+
+	menu = birb_action_menu_new();
+
+	g_signal_emit(info, signals[SIG_POPULATE_MENU], 0, menu);
+
+	return menu;
+}
diff --git a/libpurple/tests/test_contact_info.c b/libpurple/tests/test_contact_info.c
--- a/libpurple/tests/test_contact_info.c
+++ b/libpurple/tests/test_contact_info.c
@@ -18,6 +18,8 @@
 
 #include <glib.h>
 
+#include <birb.h>
+
 #include <purple.h>
 
 #include "test_ui.h"
@@ -462,6 +464,61 @@
 }
 
 /******************************************************************************
+ * get_menu tests
+ *****************************************************************************/
+static void
+test_purple_contact_info_get_menu_populate_cb(PurpleContactInfo *info,
+                                              BirbActionMenu *action_menu,
+                                              gpointer data)
+{
+	GMenu *menu = NULL;
+	guint *counter = data;
+
+	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, "testing", "app.test");
+
+	/* Increment our called counter. */
+	*counter = *counter + 1;
+}
+
+static void
+test_purple_contact_info_get_menu(void) {
+	PurpleContactInfo *info = NULL;
+	BirbActionMenu *action_menu = NULL;
+	GMenu *menu = NULL;
+	guint counter = 0;
+
+	info = purple_contact_info_new(NULL);
+
+	/* Check that we get an empty menu back with no signal handlers. */
+	action_menu = purple_contact_info_get_menu(info);
+	menu = birb_action_menu_get_menu(action_menu);
+	g_assert_true(G_IS_MENU_MODEL(menu));
+	g_assert_cmpuint(g_menu_model_get_n_items(G_MENU_MODEL(menu)), ==, 0);
+	g_clear_object(&action_menu);
+
+	/* Add a signal handler and verify that our items got added. */
+	g_signal_connect(info, "populate-menu",
+	                 G_CALLBACK(test_purple_contact_info_get_menu_populate_cb),
+	                 &counter);
+
+	action_menu = purple_contact_info_get_menu(info);
+	menu = birb_action_menu_get_menu(action_menu);
+	g_assert_cmpuint(counter, ==, 1);
+	g_assert_true(G_IS_MENU_MODEL(menu));
+	g_assert_cmpuint(g_menu_model_get_n_items(G_MENU_MODEL(menu)), ==, 1);
+
+	g_assert_finalize_object(action_menu);
+
+	/* Cleanup. */
+	g_clear_object(&info);
+}
+
+/******************************************************************************
  * Main
  *****************************************************************************/
 gint
@@ -517,6 +574,9 @@
 	g_test_add_func("/contact-info/presence-changed-signal",
 	                test_purple_contact_info_presence_changed_signal);
 
+	g_test_add_func("/contact-info/get_menu",
+	                test_purple_contact_info_get_menu);
+
 	ret = g_test_run();
 
 	test_ui_purple_uninit();
diff --git a/meson.build b/meson.build
--- a/meson.build
+++ b/meson.build
@@ -257,7 +257,7 @@
 #######################################################################
 # Check for birb glib utility library
 #######################################################################
-birb = dependency('birb')
+birb_dep = dependency('birb')
 
 #######################################################################
 # Check for Native Avahi headers (for Bonjour)
diff --git a/subprojects/birb.wrap b/subprojects/birb.wrap
--- a/subprojects/birb.wrap
+++ b/subprojects/birb.wrap
@@ -1,4 +1,4 @@
 [wrap-hg]
 directory = birb
 url = https://keep.imfreedom.org/birb/birb/
-revision = d9d91dc0265d
+revision = a39448f829f9
