Thu, 07 Aug 2025 21:40:13 -0500
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/
--- 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", ¬e, - "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(¬e, 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(¬e, 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",