# HG changeset patch
# Node ID 3c3c972085ce6bffa6d33cba54e471007caa42b8
# Parent  d01d9aeec4368319d6c345b6a6ceba34ae1e2afb
diff --git a/libpurple/purpleconversationmember.c b/libpurple/purpleconversationmember.c
--- a/libpurple/purpleconversationmember.c
+++ b/libpurple/purpleconversationmember.c
@@ -351,6 +351,45 @@
 /******************************************************************************
  * Public API
  *****************************************************************************/
+int
+purple_conversation_member_compare(PurpleConversationMember *member1,
+                                   PurpleConversationMember *member2)
+{
+	const char *name1 = NULL;
+	const char *name2 = NULL;
+	int result = 0;
+
+	/* Shortcut if the pointer is the same. */
+	if(member1 == member2) {
+		return 0;
+	}
+
+	/* NULL checks. */
+	if(PURPLE_IS_CONVERSATION_MEMBER(member1) &&
+	   !PURPLE_IS_CONVERSATION_MEMBER(member2))
+	{
+		return -1;
+	}
+
+	if(!PURPLE_IS_CONVERSATION_MEMBER(member1) &&
+	   PURPLE_IS_CONVERSATION_MEMBER(member2))
+	{
+		return 1;
+	}
+
+	/* Check badges first. */
+	result = purple_badges_compare(member1->badges, member2->badges);
+	if(result != 0) {
+		return result;
+	}
+
+	/* Sort by the name for display. */
+	name1 = purple_conversation_member_get_name_for_display(member1);
+	name2 = purple_conversation_member_get_name_for_display(member2);
+
+	return purple_utf8_strcasecmp(name1, name2);
+}
+
 PurpleConversationMember *
 purple_conversation_member_new(PurpleContactInfo *info) {
 	g_return_val_if_fail(PURPLE_IS_CONTACT_INFO(info), NULL);
diff --git a/libpurple/purpleconversationmember.h b/libpurple/purpleconversationmember.h
--- a/libpurple/purpleconversationmember.h
+++ b/libpurple/purpleconversationmember.h
@@ -60,6 +60,25 @@
  */
 
 /**
+ * purple_conversation_member_compare:
+ * @member2: (nullable): the member to compare
+ *
+ * Compares two members for sorting order.
+ *
+ * This starts by calling [method@Badges.compare] on the
+ * [property@ConversationMember:badges] property and then checks the
+ * [property@ConversationMember:name-for-display].
+ *
+ * Returns: less than zero if the first member should be sorted first, 0 if the
+ *          two are equal or greater than zero if the second member should be
+ *          sorted first.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+int purple_conversation_member_compare(PurpleConversationMember *member1, PurpleConversationMember *member2);
+
+/**
  * purple_conversation_member_new:
  * @info: The [class@Purple.ContactInfo] for the member.
  *
diff --git a/libpurple/tests/test_conversation_member.c b/libpurple/tests/test_conversation_member.c
--- a/libpurple/tests/test_conversation_member.c
+++ b/libpurple/tests/test_conversation_member.c
@@ -345,6 +345,132 @@
 }
 
 /******************************************************************************
+ * Compare Tests
+ *****************************************************************************/
+static void
+test_purple_conversation_member_compare_not_null__null(void) {
+	PurpleContactInfo *info = NULL;
+	PurpleConversationMember *member = NULL;
+
+	info = purple_contact_info_new(NULL);
+	member = purple_conversation_member_new(info);
+
+	g_assert_cmpint(purple_conversation_member_compare(member, NULL), <, 0);
+
+	g_assert_finalize_object(member);
+	g_assert_finalize_object(info);
+}
+
+static void
+test_purple_conversation_member_compare_null__not_null(void) {
+	PurpleContactInfo *info = NULL;
+	PurpleConversationMember *member = NULL;
+
+	info = purple_contact_info_new(NULL);
+	member = purple_conversation_member_new(info);
+
+	g_assert_cmpint(purple_conversation_member_compare(NULL, member), >, 0);
+
+	g_assert_finalize_object(member);
+	g_assert_finalize_object(info);
+}
+
+static void
+test_purple_conversation_member_compare_null__null(void) {
+	g_assert_cmpint(purple_conversation_member_compare(NULL, NULL), ==, 0);
+}
+
+static void
+test_purple_conversation_member_compare_same(void) {
+	PurpleContactInfo *info = NULL;
+	PurpleConversationMember *member = NULL;
+
+	info = purple_contact_info_new(NULL);
+	member = purple_conversation_member_new(info);
+
+	g_assert_cmpint(purple_conversation_member_compare(member, member), ==, 0);
+
+	g_assert_finalize_object(member);
+	g_assert_finalize_object(info);
+}
+
+static void
+test_purple_conversation_member_compare_nickname__nickname(void) {
+	PurpleContactInfo *info1 = NULL;
+	PurpleContactInfo *info2 = NULL;
+	PurpleConversationMember *member1 = NULL;
+	PurpleConversationMember *member2 = NULL;
+	int result = 0;
+
+	info1 = purple_contact_info_new(NULL);
+	member1 = purple_conversation_member_new(info1);
+	purple_conversation_member_set_nickname(member1, "aaa");
+
+	info2 = purple_contact_info_new(NULL);
+	member2 = purple_conversation_member_new(info2);
+	purple_conversation_member_set_nickname(member2, "zzz");
+
+	result = purple_conversation_member_compare(member1, member2);
+	g_assert_cmpint(result, <, 0);
+
+	result = purple_conversation_member_compare(member2, member1);
+	g_assert_cmpint(result, >, 0);
+
+	purple_conversation_member_set_nickname(member2, "aaa");
+	result = purple_conversation_member_compare(member1, member2);
+	g_assert_cmpint(result, ==, 0);
+
+	g_assert_finalize_object(member1);
+	g_assert_finalize_object(member2);
+	g_assert_finalize_object(info1);
+	g_assert_finalize_object(info2);
+}
+
+static void
+test_purple_conversation_member_compare_badges__nickname(void) {
+	PurpleBadge *badge = NULL;
+	PurpleBadges *badges = NULL;
+	PurpleContactInfo *info1 = NULL;
+	PurpleContactInfo *info2 = NULL;
+	PurpleConversationMember *member1 = NULL;
+	PurpleConversationMember *member2 = NULL;
+	gboolean removed = FALSE;
+	int result = 0;
+
+	info1 = purple_contact_info_new(NULL);
+	member1 = purple_conversation_member_new(info1);
+	purple_conversation_member_set_nickname(member1, "zzz");
+	badges = purple_conversation_member_get_badges(member1);
+	badge = purple_badge_new("id", 1000, "icon", "I");
+	purple_badges_add_badge(badges, badge);
+	g_clear_object(&badge);
+
+	info2 = purple_contact_info_new(NULL);
+	member2 = purple_conversation_member_new(info2);
+	purple_conversation_member_set_nickname(member2, "aaa");
+
+	result = purple_conversation_member_compare(member1, member2);
+	g_assert_cmpint(result, <, 0);
+
+	result = purple_conversation_member_compare(member2, member1);
+	g_assert_cmpint(result, >, 0);
+
+	/* Remove the badge and make sure member2 is sorted first because its
+	 * nickname sorts first.
+	 */
+	removed = purple_badges_remove_badge(badges, "id");
+	g_assert_true(removed);
+
+	result = purple_conversation_member_compare(member1, member2);
+	g_assert_cmpint(result, >, 0);
+
+	g_assert_finalize_object(member1);
+	g_assert_finalize_object(member2);
+	g_assert_finalize_object(info1);
+	g_assert_finalize_object(info2);
+}
+
+/******************************************************************************
  * Main
  *****************************************************************************/
 int
@@ -373,6 +499,18 @@
 	g_test_add_func("/conversation-member/matches/contact_info",
 	                test_purple_conversation_member_matches_contact_info);
 
+	g_test_add_func("/conversation-member/compare/not_null__null",
+	                test_purple_conversation_member_compare_not_null__null);
+	g_test_add_func("/conversation-member/compare/null__not_null",
+	                test_purple_conversation_member_compare_null__not_null);
+	g_test_add_func("/conversation-member/compare/null__null",
+	                test_purple_conversation_member_compare_null__null);
+	g_test_add_func("/conversation-member/compare/same",
+	                test_purple_conversation_member_compare_same);
+	g_test_add_func("/conversation-member/compare/nickname__nickname",
+	                test_purple_conversation_member_compare_nickname__nickname);
+	g_test_add_func("/conversation-member/compare/badges__nickname",
+	                test_purple_conversation_member_compare_badges__nickname);
 
 	return g_test_run();
 }
