Add an avatar-for-display property to Purple.ContactInfo

Thu, 07 Aug 2025 21:40:13 -0500

author
Gary Kramlich <grim@reaperworld.com>
date
Thu, 07 Aug 2025 21:40:13 -0500
changeset 43302
e7b0bbfec5d5
parent 43301
0e43dc8462e8
child 43303
cddf3066f1bc

Add an avatar-for-display property to Purple.ContactInfo

Testing Done:
Ran the tests under valgrind and called in the turtles.

Reviewed at https://reviews.imfreedom.org/r/4086/

libpurple/purplecontactinfo.c file | annotate | diff | comparison | revisions
libpurple/purplecontactinfo.h file | annotate | diff | comparison | revisions
libpurple/tests/test_contact_info.c file | annotate | diff | comparison | revisions
--- a/libpurple/purplecontactinfo.c	Thu Aug 07 21:34:33 2025 -0500
+++ b/libpurple/purplecontactinfo.c	Thu Aug 07 21:40:13 2025 -0500
@@ -65,6 +65,7 @@
 	PROP_TIME_ZONE,
 	PROP_NOTE,
 	PROP_AVATAR,
+	PROP_AVATAR_FOR_DISPLAY,
 	PROP_PRESENCE,
 	PROP_TAGS,
 	PROP_PERSON,
@@ -200,6 +201,9 @@
 		case PROP_AVATAR:
 			g_value_set_object(value, purple_contact_info_get_avatar(info));
 			break;
+		case PROP_AVATAR_FOR_DISPLAY:
+			g_value_set_object(value, purple_contact_info_get_avatar_for_display(info));
+			break;
 		case PROP_PRESENCE:
 			g_value_set_object(value, purple_contact_info_get_presence(info));
 			break;
@@ -502,6 +506,18 @@
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
+	 * PurpleContactInfo:avatar-for-display:
+	 *
+	 * The avatar that should be displayed for this contact info.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_AVATAR_FOR_DISPLAY] = g_param_spec_object(
+		"avatar-for-display", NULL, NULL,
+		PURPLE_TYPE_IMAGE,
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	/**
 	 * PurpleContactInfo:presence:
 	 *
 	 * The [class@Purple.Presence] for this contact. This is typically
@@ -958,6 +974,26 @@
 	return priv->avatar;
 }
 
+PurpleImage *
+purple_contact_info_get_avatar_for_display(PurpleContactInfo *info) {
+	PurpleContactInfoPrivate *priv = NULL;
+	PurpleImage *candidate = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_CONTACT_INFO(info), NULL);
+
+	priv = purple_contact_info_get_instance_private(info);
+
+	if(PURPLE_IS_PERSON(priv->person)) {
+		candidate = purple_person_get_avatar_for_display(priv->person);
+	}
+
+	if(!PURPLE_IS_IMAGE(candidate)) {
+		candidate = priv->avatar;
+	}
+
+	return candidate;
+}
+
 void
 purple_contact_info_set_avatar(PurpleContactInfo *info, PurpleImage *avatar) {
 	PurpleContactInfoPrivate *priv = NULL;
@@ -967,7 +1003,12 @@
 	priv = purple_contact_info_get_instance_private(info);
 
 	if(g_set_object(&priv->avatar, avatar)) {
-		g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_AVATAR]);
+		GObject *obj = G_OBJECT(info);
+
+		g_object_freeze_notify(obj);
+		g_object_notify_by_pspec(obj, properties[PROP_AVATAR]);
+		g_object_notify_by_pspec(obj, properties[PROP_AVATAR_FOR_DISPLAY]);
+		g_object_thaw_notify(obj);
 	}
 }
 
@@ -1017,6 +1058,8 @@
 		g_object_freeze_notify(G_OBJECT(info));
 
 		g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_PERSON]);
+		g_object_notify_by_pspec(G_OBJECT(info),
+		                         properties[PROP_AVATAR_FOR_DISPLAY]);
 
 		/* Update the name-for-display property */
 		purple_contact_info_update_name_for_display(info);
--- a/libpurple/purplecontactinfo.h	Thu Aug 07 21:34:33 2025 -0500
+++ b/libpurple/purplecontactinfo.h	Thu Aug 07 21:40:13 2025 -0500
@@ -359,6 +359,23 @@
 PurpleImage *purple_contact_info_get_avatar(PurpleContactInfo *info);
 
 /**
+ * purple_contact_info_get_avatar_for_display:
+ *
+ * Get the avatar that should be displayed for this contact info.
+ *
+ * If [property@ContactInfo:person] is set and its [property@Person:avatar] is
+ * set, that will be returned.
+ *
+ * Otherwise the value of [property@PurpleContactInfo:avatar] will be returned.
+ *
+ * Returns: (transfer none) (nullable): the avatar.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+PurpleImage *purple_contact_info_get_avatar_for_display(PurpleContactInfo *info);
+
+/**
  * purple_contact_info_set_avatar:
  * @info: The instance.
  * @avatar: (nullable): The new avatar to set.
--- a/libpurple/tests/test_contact_info.c	Thu Aug 07 21:34:33 2025 -0500
+++ b/libpurple/tests/test_contact_info.c	Thu Aug 07 21:40:13 2025 -0500
@@ -42,6 +42,7 @@
 	PurpleContactInfoPermission permission;
 	PurpleImage *avatar = NULL;
 	PurpleImage *avatar1 = NULL;
+	PurpleImage *avatar_for_display = NULL;
 	PurplePerson *person = NULL;
 	PurplePerson *person1 = NULL;
 	PurplePresence *presence1 = NULL;
@@ -91,69 +92,135 @@
 
 	/* Now use g_object_get to read all of the properties. */
 	g_object_get(info,
-		"id", &id,
-		"username", &username,
+		"alias", &alias,
+		"avatar", &avatar1,
+		"avatar-for-display", &avatar_for_display,
+		"color", &color,
 		"display-name", &display_name,
-		"alias", &alias,
-		"color", &color,
 		"email", &email,
-		"phone-number", &phone_number,
-		"time-zone", &time_zone1,
+		"external", &external,
+		"favorite", &favorite,
+		"id", &id,
+		"name-for-display", &name_for_display,
 		"note", &note,
-		"avatar", &avatar1,
-		"presence", &presence1,
-		"tags", &tags,
+		"permission", &permission,
 		"person", &person1,
-		"permission", &permission,
+		"phone-number", &phone_number,
+		"presence", &presence1,
 		"sid", &sid,
-		"name-for-display", &name_for_display,
-		"favorite", &favorite,
-		"external", &external,
+		"tags", &tags,
+		"time-zone", &time_zone1,
+		"username", &username,
 		NULL);
 
-	/* Compare all the things. */
-	g_assert_cmpstr(id, ==, "id1");
-	g_assert_cmpstr(username, ==, "username");
+	g_assert_cmpstr(alias, ==, "alias");
+	g_clear_pointer(&alias, g_free);
+
+	g_assert_true(avatar1 == avatar);
+	g_clear_object(&avatar1);
+
+	g_assert_true(avatar_for_display == avatar);
+	g_clear_object(&avatar_for_display);
+
+	g_assert_cmpstr(color, ==, "#e9c636");
+	g_clear_pointer(&color, g_free);
+
 	g_assert_cmpstr(display_name, ==, "display-name");
-	g_assert_cmpstr(alias, ==, "alias");
-	g_assert_cmpstr(color, ==, "#e9c636");
+	g_clear_pointer(&display_name, g_free);
+
 	g_assert_cmpstr(email, ==, "pidgin@example.com");
+	g_clear_pointer(&email, g_free);
+
+	g_assert_true(external);
+
+	g_assert_true(favorite);
+
+	g_assert_cmpstr(id, ==, "id1");
+	g_clear_pointer(&id, g_free);
+
+	g_assert_cmpstr(name_for_display, ==, "alias");
+	g_clear_pointer(&name_for_display, g_free);
+
+	g_assert_cmpstr(note, ==, "message in a bottle");
+	g_clear_pointer(&note, g_free);
+
 	g_assert_cmpstr(phone_number, ==, "+10123456789");
+	g_clear_pointer(&phone_number, g_free);
+
+	g_assert_true(permission == PURPLE_CONTACT_INFO_PERMISSION_ALLOW);
+
+	g_assert_true(person1 == person);
+	g_clear_object(&person1);
+
+	g_assert_nonnull(presence1);
+	g_clear_object(&presence1);
+
+	g_assert_cmpstr(sid, ==, "sid");
+	g_clear_pointer(&sid, g_free);
+
+	g_assert_nonnull(tags);
+	g_clear_object(&tags);
+
 	g_assert_cmpstr(g_time_zone_get_identifier(time_zone1), ==,
 	                g_time_zone_get_identifier(time_zone));
-	g_assert_cmpstr(note, ==, "message in a bottle");
-	g_assert_cmpstr(name_for_display, ==, "alias");
-	g_assert_true(avatar1 == avatar);
-	g_assert_nonnull(presence1);
-	g_assert_nonnull(tags);
-	g_assert_true(person1 == person);
-	g_assert_true(permission == PURPLE_CONTACT_INFO_PERMISSION_ALLOW);
-	g_assert_cmpstr(sid, ==, "sid");
-	g_assert_true(favorite);
-	g_assert_true(external);
+	g_clear_pointer(&time_zone, g_time_zone_unref);
 
-	/* Free/unref all the things. */
-	g_clear_pointer(&id, g_free);
+	g_assert_cmpstr(username, ==, "username");
 	g_clear_pointer(&username, g_free);
-	g_clear_pointer(&display_name, g_free);
-	g_clear_pointer(&alias, g_free);
-	g_clear_pointer(&color, g_free);
-	g_clear_pointer(&email, g_free);
-	g_clear_pointer(&phone_number, g_free);
-	g_clear_pointer(&time_zone, g_time_zone_unref);
-	g_clear_pointer(&note, g_free);
-	g_clear_pointer(&name_for_display, g_free);
-	g_clear_pointer(&sid, g_free);
-	g_clear_object(&avatar1);
-	g_clear_object(&presence1);
-	g_clear_object(&tags);
+
 	g_clear_object(&person);
-	g_clear_object(&person1);
-
 	g_clear_object(&avatar);
 	g_clear_object(&info);
 }
 
+static void
+test_purple_contact_info_get_avatar_for_display(void) {
+	PurpleContactInfo *info = NULL;
+	PurpleImage *avatar1 = NULL;
+	PurpleImage *contact_avatar = NULL;
+	PurpleImage *person_avatar = NULL;
+	PurplePerson *person = NULL;
+
+	contact_avatar = g_object_new(PURPLE_TYPE_IMAGE, NULL);
+
+	info = purple_contact_info_new(NULL);
+	purple_contact_info_set_avatar(info, contact_avatar);
+
+	/* Make sure the contact info's avatar gets returned if there is no
+	 * associated person.
+	 */
+	avatar1 = purple_contact_info_get_avatar_for_display(info);
+	g_assert_true(avatar1 == contact_avatar);
+
+	/* Create a person and make sure we get the contact's avatar back. */
+	person = purple_person_new();
+	purple_person_add_contact_info(person, info);
+
+	avatar1 = purple_contact_info_get_avatar_for_display(info);
+	g_assert_true(avatar1 == contact_avatar);
+
+	/* Now set an avatar on the person and verify that it gets returned. */
+	person_avatar = g_object_new(PURPLE_TYPE_IMAGE, NULL);
+	purple_person_set_avatar(person, person_avatar);
+
+	avatar1 = purple_contact_info_get_avatar_for_display(info);
+	g_assert_true(avatar1 == person_avatar);
+
+	/* Remove the contact info from the person and verify that the contact
+	 * avatar gets returned again.
+	 */
+	purple_contact_info_set_person(info, NULL);
+
+	avatar1 = purple_contact_info_get_avatar_for_display(info);
+	g_assert_true(avatar1 == contact_avatar);
+
+	/* Cleanup */
+	g_assert_finalize_object(person);
+	g_assert_finalize_object(info);
+	g_assert_finalize_object(contact_avatar);
+	g_assert_finalize_object(person_avatar);
+}
+
 /******************************************************************************
  * get_name_for_display tests
  *****************************************************************************/
@@ -587,6 +654,9 @@
 	g_test_add_func("/contact-info/properties",
 	                test_purple_contact_info_properties);
 
+	g_test_add_func("/contact-info/get-avatar-for-display",
+	                test_purple_contact_info_get_avatar_for_display);
+
 	g_test_add_func("/contact-info/get_name_for_display/person_with_alias",
 	                test_purple_contact_info_get_name_for_display_person_with_alias);
 	g_test_add_func("/contact-info/get_name_for_display/contact_with_alias",

mercurial