pidgin/pidgin

b7f9345b02a4
Parents d3c594113fe1
Children ae3fa963c1b3
Add a name-for-display property to PurpleContactInfo

This is a readonly property that is updated when the other properties change.
I also tweaked priorities a bit, a contact info's alias is now the highest
priority to allow users to set an alias on a person, but then also override it
at the contact info level. The use case here is person has an alias of "Pidgy",
but Pidgy's slack account is aliased to "Pidgy (work)".

Testing Done:
Ran the unit tests.

Reviewed at https://reviews.imfreedom.org/r/2151/
--- a/libpurple/purplecontactinfo.c Thu Jan 05 22:41:48 2023 -0600
+++ b/libpurple/purplecontactinfo.c Fri Jan 06 01:53:58 2023 -0600
@@ -22,12 +22,14 @@
#include "util.h"
typedef struct {
- gchar *id;
+ char *id;
- gchar *username;
- gchar *display_name;
- gchar *alias;
- gchar *color;
+ char *username;
+ char *display_name;
+ char *alias;
+ char *color;
+
+ char *name_for_display;
GdkPixbuf *avatar;
@@ -52,6 +54,7 @@
PROP_TAGS,
PROP_PERSON,
PROP_PERMISSION,
+ PROP_NAME_FOR_DISPLAY,
N_PROPERTIES
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };
@@ -60,6 +63,70 @@
G_TYPE_OBJECT)
/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+purple_contact_info_update_name_for_display(PurpleContactInfo *info) {
+ PurpleContactInfoPrivate *priv = NULL;
+ const char *name_for_display = NULL;
+
+ priv = purple_contact_info_get_instance_private(info);
+
+ /* If the info has an alias set, use it. */
+ if(name_for_display == NULL && !purple_strempty(priv->alias)) {
+ name_for_display = priv->alias;
+ }
+
+ /* If info is associated with a PurplePerson that has an alias set, use the
+ * alias of that PurplePerson.
+ */
+ if(name_for_display == NULL && priv->person != NULL) {
+ const char *alias = purple_person_get_alias(priv->person);
+
+ if(!purple_strempty(alias)) {
+ name_for_display = alias;
+ }
+ }
+
+ /* If the info has a display name set, use it. */
+ if(name_for_display == NULL && !purple_strempty(priv->display_name)) {
+ name_for_display = priv->display_name;
+ }
+
+ /* Fallback to the username if that is set. */
+ if(name_for_display == NULL && !purple_strempty(priv->username)) {
+ name_for_display = priv->username;
+ }
+
+ /* Finally, in a last ditch effort, use the id of the info. */
+ if(name_for_display == NULL) {
+ name_for_display = priv->id;
+ }
+
+ if(!purple_strequal(name_for_display, priv->name_for_display)) {
+ /* If we have a new name for display, free the old one, dup the new one
+ * into the struct, and then emit the notify signal.
+ */
+ g_free(priv->name_for_display);
+ priv->name_for_display = g_strdup(name_for_display);
+
+ g_object_notify_by_pspec(G_OBJECT(info),
+ properties[PROP_NAME_FOR_DISPLAY]);
+ }
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+purple_contact_info_person_alias_changed_cb(G_GNUC_UNUSED GObject *obj,
+ G_GNUC_UNUSED GParamSpec *pspec,
+ gpointer data)
+{
+ purple_contact_info_update_name_for_display(data);
+}
+
+/******************************************************************************
* GObject Implementation
*****************************************************************************/
static void
@@ -100,6 +167,10 @@
case PROP_PERMISSION:
g_value_set_enum(value, purple_contact_info_get_permission(info));
break;
+ case PROP_NAME_FOR_DISPLAY:
+ g_value_set_string(value,
+ purple_contact_info_get_name_for_display(info));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
@@ -170,6 +241,7 @@
g_clear_pointer(&priv->username, g_free);
g_clear_pointer(&priv->display_name, g_free);
g_clear_pointer(&priv->alias, g_free);
+ g_clear_pointer(&priv->name_for_display, g_free);
G_OBJECT_CLASS(purple_contact_info_parent_class)->finalize(obj);
}
@@ -187,6 +259,8 @@
if(priv->id == NULL) {
purple_contact_info_set_id(info, NULL);
}
+
+ purple_contact_info_update_name_for_display(info);
}
static void
@@ -348,6 +422,33 @@
PURPLE_CONTACT_INFO_PERMISSION_UNSET,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ /**
+ * PurpleContactInfo:name-for-display:
+ *
+ * The name that the user interface should display for this contact info.
+ *
+ * This will first check [property@Purple.ContactInfo:alias] and return
+ * that if it is set.
+ *
+ * Next, if the [property@Purple.ContactInfo:person] points to a valid
+ * [class@Purple.Person], the alias of [class@Purple.Person] will be
+ * returned if it is set.
+ *
+ * Otherwise, this will be set to the first set property from the following
+ * list:
+ *
+ * * [property@Purple.ContactInfo:display-name]
+ * * [property@Purple.ContactInfo:username]
+ * * [property@Purple.ContactInfo:id]
+ *
+ * Since: 3.0.0
+ */
+ properties[PROP_NAME_FOR_DISPLAY] = g_param_spec_string(
+ "name-for-display", "name-for-display",
+ "The name that should be displayed for the contact info",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}
@@ -376,15 +477,26 @@
void
purple_contact_info_set_id(PurpleContactInfo *info, const gchar *id) {
PurpleContactInfoPrivate *priv = NULL;
+ gboolean changed = FALSE;
g_return_if_fail(PURPLE_IS_CONTACT_INFO(info));
priv = purple_contact_info_get_instance_private(info);
+ changed = !purple_strequal(priv->id, id);
+
g_free(priv->id);
priv->id = g_strdup(id);
- g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_ID]);
+ if(changed) {
+ g_object_freeze_notify(G_OBJECT(info));
+
+ g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_ID]);
+
+ purple_contact_info_update_name_for_display(info);
+
+ g_object_thaw_notify(G_OBJECT(info));
+ }
}
const gchar *
@@ -403,15 +515,26 @@
const gchar *username)
{
PurpleContactInfoPrivate *priv = NULL;
+ gboolean changed = FALSE;
g_return_if_fail(PURPLE_IS_CONTACT_INFO(info));
priv = purple_contact_info_get_instance_private(info);
+ changed = !purple_strequal(priv->username, username);
+
g_free(priv->username);
priv->username = g_strdup(username);
- g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_USERNAME]);
+ if(changed) {
+ g_object_freeze_notify(G_OBJECT(info));
+
+ g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_USERNAME]);
+
+ purple_contact_info_update_name_for_display(info);
+
+ g_object_thaw_notify(G_OBJECT(info));
+ }
}
const gchar *
@@ -430,15 +553,26 @@
const gchar *display_name)
{
PurpleContactInfoPrivate *priv = NULL;
+ gboolean changed = FALSE;
g_return_if_fail(PURPLE_IS_CONTACT_INFO(info));
priv = purple_contact_info_get_instance_private(info);
+ changed = !purple_strequal(priv->display_name, display_name);
+
g_free(priv->display_name);
priv->display_name = g_strdup(display_name);
- g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_DISPLAY_NAME]);
+ if(changed) {
+ g_object_freeze_notify(G_OBJECT(info));
+
+ g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_DISPLAY_NAME]);
+
+ purple_contact_info_update_name_for_display(info);
+
+ g_object_thaw_notify(G_OBJECT(info));
+ }
}
const gchar *
@@ -455,15 +589,26 @@
void
purple_contact_info_set_alias(PurpleContactInfo *info, const gchar *alias) {
PurpleContactInfoPrivate *priv = NULL;
+ gboolean changed = FALSE;
g_return_if_fail(PURPLE_IS_CONTACT_INFO(info));
priv = purple_contact_info_get_instance_private(info);
+ changed = !purple_strequal(priv->alias, alias);
+
g_free(priv->alias);
priv->alias = g_strdup(alias);
- g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_ALIAS]);
+ if(changed) {
+ g_object_freeze_notify(G_OBJECT(info));
+
+ g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_ALIAS]);
+
+ purple_contact_info_update_name_for_display(info);
+
+ g_object_thaw_notify(G_OBJECT(info));
+ }
}
const char *
@@ -548,7 +693,26 @@
priv = purple_contact_info_get_instance_private(info);
if(g_set_object(&priv->person, person)) {
+ /* If we got a new person, we need to connect to the notify::alias
+ * signal.
+ */
+ if(PURPLE_IS_PERSON(priv->person)) {
+ g_signal_connect_object(priv->person, "notify::alias",
+ G_CALLBACK(purple_contact_info_person_alias_changed_cb),
+ info, 0);
+ }
+
+ /* Freeze notifications as the person update could change the
+ * name-for-display property.
+ */
+ g_object_freeze_notify(G_OBJECT(info));
+
g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_PERSON]);
+
+ /* Update the name-for-display property */
+ purple_contact_info_update_name_for_display(info);
+
+ g_object_thaw_notify(G_OBJECT(info));
}
}
@@ -598,34 +762,7 @@
priv = purple_contact_info_get_instance_private(info);
- /* If info is associated with a PurplePerson that has an alias set,
- * return the alias of that PurplePerson.
- */
- if(priv->person != NULL) {
- const char *alias = purple_person_get_alias(priv->person);
-
- if(alias != NULL && alias[0] != '\0') {
- return alias;
- }
- }
-
- /* If the purple user set an alias for the info, return that. */
- if(priv->alias != NULL && priv->alias[0] != '\0') {
- return priv->alias;
- }
-
- /* If the info has a display name set, return that. */
- if(priv->display_name != NULL && priv->display_name[0] != '\0') {
- return priv->display_name;
- }
-
- /* Fallback to the username if that is set. */
- if(priv->username != NULL && priv->username[0] != '\0') {
- return priv->username;
- }
-
- /* Finally, in a last ditch effort, return the id of the info. */
- return priv->id;
+ return priv->name_for_display;
}
int
--- a/libpurple/purplecontactinfo.h Thu Jan 05 22:41:48 2023 -0600
+++ b/libpurple/purplecontactinfo.h Fri Jan 06 01:53:58 2023 -0600
@@ -332,17 +332,8 @@
* purple_contact_info_get_name_for_display:
* @info: The instance.
*
- * Gets the name that should be displayed for @info.
- *
- * If @info is associated with a [class@Purple.Person], the value of
- * [property@Purple.Person:alias] will be returned if it is set.
- *
- * Otherwise, this will return the first set property from the following list:
- *
- * * [property@Purple.ContactInfo:alias]
- * * [property@Purple.ContactInfo:display-name]
- * * [property@Purple.ContactInfo:username]
- * * [property@Purple.ContactInfo:id]
+ * Gets the name that should be displayed for @info. See
+ * [property@Purple.ContactInfo:name-for-display] for more information.
*
* Returns: (transfer none): The name to display for @info.
*
--- a/libpurple/tests/test_contact_info.c Thu Jan 05 22:41:48 2023 -0600
+++ b/libpurple/tests/test_contact_info.c Fri Jan 06 01:53:58 2023 -0600
@@ -46,11 +46,12 @@
PurpleTags *tags = NULL;
GdkPixbuf *avatar = NULL;
GdkPixbuf *avatar1 = NULL;
- gchar *id = NULL;
- gchar *username = NULL;
- gchar *display_name = NULL;
- gchar *alias = NULL;
- gchar *color = NULL;
+ char *id = NULL;
+ char *username = NULL;
+ char *display_name = NULL;
+ char *alias = NULL;
+ char *color = NULL;
+ char *name_for_display = NULL;
avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 1, 1);
person = purple_person_new();
@@ -83,6 +84,7 @@
"tags", &tags,
"person", &person1,
"permission", &permission,
+ "name-for-display", &name_for_display,
NULL);
/* Compare all the things. */
@@ -91,6 +93,7 @@
g_assert_cmpstr(display_name, ==, "display-name");
g_assert_cmpstr(alias, ==, "alias");
g_assert_cmpstr(color, ==, "#e9c636");
+ g_assert_cmpstr(name_for_display, ==, "alias");
g_assert_true(avatar1 == avatar);
g_assert_nonnull(presence1);
g_assert_nonnull(tags);
@@ -103,6 +106,7 @@
g_clear_pointer(&display_name, g_free);
g_clear_pointer(&alias, g_free);
g_clear_pointer(&color, g_free);
+ g_clear_pointer(&name_for_display, g_free);
g_clear_object(&avatar1);
g_clear_object(&presence1);
g_clear_object(&tags);
@@ -123,13 +127,18 @@
const char *alias = NULL;
person = purple_person_new();
- purple_person_set_alias(person, "this is the alias");
+ purple_person_set_alias(person, "person alias");
- info = purple_contact_info_new(NULL);
+ info = purple_contact_info_new("id");
+ /* we don't set the alias on the contact info, as that takes priority over
+ * the person's alias.
+ */
+ purple_contact_info_set_username(info, "username");
+ purple_contact_info_set_display_name(info, "display name");
purple_contact_info_set_person(info, person);
alias = purple_contact_info_get_name_for_display(info);
- g_assert_cmpstr(alias, ==, "this is the alias");
+ g_assert_cmpstr(alias, ==, "person alias");
g_clear_object(&info);
g_clear_object(&person);
@@ -142,14 +151,17 @@
const char *alias = NULL;
person = purple_person_new();
+ purple_person_set_alias(person, "person alias");
- info = purple_contact_info_new(NULL);
+ info = purple_contact_info_new("id");
purple_contact_info_set_person(info, person);
+ purple_contact_info_set_alias(info, "contact alias");
+ purple_contact_info_set_username(info, "username");
+ purple_contact_info_set_display_name(info, "display name");
- purple_contact_info_set_alias(info, "this is the alias");
alias = purple_contact_info_get_name_for_display(info);
- g_assert_cmpstr(alias, ==, "this is the alias");
+ g_assert_cmpstr(alias, ==, "contact alias");
g_clear_object(&info);
g_clear_object(&person);
@@ -163,13 +175,14 @@
person = purple_person_new();
- info = purple_contact_info_new(NULL);
+ info = purple_contact_info_new("id");
purple_contact_info_set_person(info, person);
- purple_contact_info_set_display_name(info, "this is the display name");
+ purple_contact_info_set_display_name(info, "display name");
+ purple_contact_info_set_username(info, "username");
alias = purple_contact_info_get_name_for_display(info);
- g_assert_cmpstr(alias, ==, "this is the display name");
+ g_assert_cmpstr(alias, ==, "display name");
g_clear_object(&info);
g_clear_object(&person);
@@ -183,7 +196,7 @@
person = purple_person_new();
- info = purple_contact_info_new(NULL);
+ info = purple_contact_info_new("id");
purple_contact_info_set_username(info, "username");
purple_contact_info_set_person(info, person);
--- a/libpurple/util.h Thu Jan 05 22:41:48 2023 -0600
+++ b/libpurple/util.h Fri Jan 06 01:53:58 2023 -0600
@@ -234,6 +234,21 @@
}
/**
+ * purple_strempty:
+ * @str: A string to check if it is empty.
+ *
+ * Determines if @str is empty. That is, if it is %NULL or an empty string.
+ *
+ * Returns: %TRUE if the @str is %NULL or an empty string.
+ *
+ * Since: 3.0.0
+ */
+static inline gboolean
+purple_strempty(const char *str) {
+ return (str == NULL || str[0] == '\0');
+}
+
+/**
* purple_normalize:
* @account: The account the string belongs to, or NULL if you do
* not know the account. If you use NULL, the string