#include <telepathy-glib/interfaces.h>
#include <telepathy-glib/util.h>
+#include <telepathy-logger/log-manager.h>
+
#include <folks/folks.h>
#include <folks/folks-telepathy.h>
-#if HAVE_GEOCLUE
+#ifdef HAVE_GEOCLUE
#include <geoclue/geoclue-geocode.h>
#endif
#include "empathy-contact.h"
+#include "empathy-individual-manager.h"
#include "empathy-utils.h"
#include "empathy-enum-types.h"
#include "empathy-marshal.h"
gchar *alias;
EmpathyAvatar *avatar;
TpConnectionPresenceType presence;
- gchar *presence_message;
guint handle;
EmpathyCapabilities capabilities;
gboolean is_user;
*/
GHashTable *location;
GHashTable *groups;
+ gchar **client_types;
} EmpathyContactPriv;
static void contact_finalize (GObject *object);
static void contact_set_property (GObject *object, guint param_id,
const GValue *value, GParamSpec *pspec);
-#if HAVE_GEOCLUE
+#ifdef HAVE_GEOCLUE
static void update_geocode (EmpathyContact *contact);
#endif
static void empathy_contact_set_location (EmpathyContact *contact,
GHashTable *location);
+static void contact_set_client_types (EmpathyContact *contact,
+ const gchar * const *types);
+
static void set_capabilities_from_tp_caps (EmpathyContact *self,
TpCapabilities *caps);
+static void contact_set_avatar (EmpathyContact *contact,
+ EmpathyAvatar *avatar);
static void contact_set_avatar_from_tp_contact (EmpathyContact *contact);
+static gboolean contact_load_avatar_cache (EmpathyContact *contact,
+ const gchar *token);
G_DEFINE_TYPE (EmpathyContact, empathy_contact, G_TYPE_OBJECT);
PROP_HANDLE,
PROP_CAPABILITIES,
PROP_IS_USER,
- PROP_LOCATION
+ PROP_LOCATION,
+ PROP_CLIENT_TYPES
};
enum {
priv->presence = presence;
g_object_notify (contact, "presence");
}
- else if (!tp_strdiff (param->name, "presence-message"))
- g_object_notify (contact, "presence-message");
else if (!tp_strdiff (param->name, "identifier"))
g_object_notify (contact, "id");
else if (!tp_strdiff (param->name, "handle"))
{
contact_set_avatar_from_tp_contact (EMPATHY_CONTACT (contact));
}
+ else if (!tp_strdiff (param->name, "client-types"))
+ {
+ contact_set_client_types (EMPATHY_CONTACT (contact),
+ tp_contact_get_client_types (tp_contact));
+ }
+}
+
+static void
+folks_persona_notify_cb (FolksPersona *folks_persona,
+ GParamSpec *param,
+ GObject *contact)
+{
+ if (!tp_strdiff (param->name, "presence-message"))
+ g_object_notify (contact, "presence-message");
}
static void
priv->account = NULL;
if (priv->persona)
- g_object_unref (priv->persona);
+ {
+ g_signal_handlers_disconnect_by_func (priv->persona,
+ folks_persona_notify_cb, object);
+ g_object_unref (priv->persona);
+ }
priv->persona = NULL;
if (priv->avatar != NULL)
G_OBJECT_CLASS (empathy_contact_parent_class)->dispose (object);
}
+static void
+contact_constructed (GObject *object)
+{
+ EmpathyContact *contact = (EmpathyContact *) object;
+ EmpathyContactPriv *priv = GET_PRIV (contact);
+ GHashTable *location;
+ TpHandle self_handle;
+ TpHandle handle;
+ const gchar * const *client_types;
+
+ if (priv->tp_contact == NULL)
+ return;
+
+ priv->presence = empathy_contact_get_presence (contact);
+
+ location = tp_contact_get_location (priv->tp_contact);
+ if (location != NULL)
+ empathy_contact_set_location (contact, location);
+
+ client_types = tp_contact_get_client_types (priv->tp_contact);
+ if (client_types != NULL)
+ contact_set_client_types (contact, client_types);
+
+ set_capabilities_from_tp_caps (contact,
+ tp_contact_get_capabilities (priv->tp_contact));
+
+ contact_set_avatar_from_tp_contact (contact);
+
+ /* Set is-user property. Note that it could still be the handle is
+ * different from the connection's self handle, in the case the handle
+ * comes from a group interface. */
+ self_handle = tp_connection_get_self_handle (
+ tp_contact_get_connection (priv->tp_contact));
+ handle = tp_contact_get_handle (priv->tp_contact);
+ empathy_contact_set_is_user (contact, self_handle == handle);
+
+ g_signal_connect (priv->tp_contact, "notify",
+ G_CALLBACK (tp_contact_notify_cb), contact);
+}
+
static void
empathy_contact_class_init (EmpathyContactClass *class)
{
object_class->dispose = contact_dispose;
object_class->get_property = contact_get_property;
object_class->set_property = contact_set_property;
+ object_class->constructed = contact_constructed;
g_object_class_install_property (object_class,
PROP_TP_CONTACT,
"Avatar image",
"The avatar image",
EMPATHY_TYPE_AVATAR,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
PROP_PRESENCE,
G_TYPE_HASH_TABLE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_CLIENT_TYPES,
+ g_param_spec_boxed ("client-types",
+ "Contact client types",
+ "Client types of the contact",
+ G_TYPE_STRV,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
signals[PRESENCE_CHANGED] =
g_signal_new ("presence-changed",
G_TYPE_FROM_CLASS (class),
contact->priv = priv;
priv->location = NULL;
+ priv->client_types = NULL;
priv->groups = NULL;
}
g_hash_table_destroy (priv->groups);
g_free (priv->alias);
g_free (priv->id);
- g_free (priv->presence_message);
+ g_strfreev (priv->client_types);
G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object);
}
static void
-set_tp_contact (EmpathyContact *contact,
- TpContact *tp_contact)
+empathy_contact_set_capabilities (EmpathyContact *contact,
+ EmpathyCapabilities capabilities)
{
- EmpathyContactPriv *priv = GET_PRIV (contact);
- GHashTable *location;
- TpHandle self_handle;
- TpHandle handle;
+ EmpathyContactPriv *priv;
+
+ g_return_if_fail (EMPATHY_IS_CONTACT (contact));
+
+ priv = GET_PRIV (contact);
- if (tp_contact == NULL)
+ if (priv->capabilities == capabilities)
return;
- g_assert (priv->tp_contact == NULL);
- priv->tp_contact = g_object_ref (tp_contact);
- priv->presence = empathy_contact_get_presence (contact);
+ priv->capabilities = capabilities;
- location = tp_contact_get_location (tp_contact);
- if (location != NULL)
- empathy_contact_set_location (contact, location);
+ g_object_notify (G_OBJECT (contact), "capabilities");
+}
- set_capabilities_from_tp_caps (contact,
- tp_contact_get_capabilities (tp_contact));
+static void
+empathy_contact_set_id (EmpathyContact *contact,
+ const gchar *id)
+{
+ EmpathyContactPriv *priv;
- contact_set_avatar_from_tp_contact (contact);
+ g_return_if_fail (EMPATHY_IS_CONTACT (contact));
+ g_return_if_fail (id != NULL);
- /* Set is-user property. Note that it could still be the handle is
- * different from the connection's self handle, in the case the handle
- * comes from a group interface. */
- self_handle = tp_connection_get_self_handle (
- tp_contact_get_connection (tp_contact));
- handle = tp_contact_get_handle (tp_contact);
- empathy_contact_set_is_user (contact, self_handle == handle);
+ priv = GET_PRIV (contact);
- g_signal_connect (priv->tp_contact, "notify",
- G_CALLBACK (tp_contact_notify_cb), contact);
+ /* We temporally ref the contact because it could be destroyed
+ * during the signal emition */
+ g_object_ref (contact);
+ if (tp_strdiff (id, priv->id))
+ {
+ g_free (priv->id);
+ priv->id = g_strdup (id);
+
+ g_object_notify (G_OBJECT (contact), "id");
+ if (EMP_STR_EMPTY (priv->alias))
+ g_object_notify (G_OBJECT (contact), "alias");
+ }
+
+ g_object_unref (contact);
+}
+
+static void
+empathy_contact_set_presence (EmpathyContact *contact,
+ TpConnectionPresenceType presence)
+{
+ EmpathyContactPriv *priv;
+ TpConnectionPresenceType old_presence;
+
+ g_return_if_fail (EMPATHY_IS_CONTACT (contact));
+
+ priv = GET_PRIV (contact);
+
+ if (presence == priv->presence)
+ return;
+
+ old_presence = priv->presence;
+ priv->presence = presence;
+
+ g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence, old_presence);
+
+ g_object_notify (G_OBJECT (contact), "presence");
+}
+
+static void
+empathy_contact_set_presence_message (EmpathyContact *contact,
+ const gchar *message)
+{
+ EmpathyContactPriv *priv = GET_PRIV (contact);
+
+ g_return_if_fail (EMPATHY_IS_CONTACT (contact));
+
+ if (priv->persona != NULL)
+ {
+ folks_presence_set_presence_message (FOLKS_PRESENCE (priv->persona),
+ message);
+ }
+}
+
+static void
+empathy_contact_set_handle (EmpathyContact *contact,
+ guint handle)
+{
+ EmpathyContactPriv *priv;
+
+ g_return_if_fail (EMPATHY_IS_CONTACT (contact));
+
+ priv = GET_PRIV (contact);
+
+ g_object_ref (contact);
+ if (handle != priv->handle)
+ {
+ priv->handle = handle;
+ g_object_notify (G_OBJECT (contact), "handle");
+ }
+ g_object_unref (contact);
}
static void
switch (param_id)
{
case PROP_TP_CONTACT:
- set_tp_contact (contact, g_value_get_object (value));
+ priv->tp_contact = g_value_dup_object (value);
break;
case PROP_ACCOUNT:
g_assert (priv->account == NULL);
case PROP_ALIAS:
empathy_contact_set_alias (contact, g_value_get_string (value));
break;
- case PROP_AVATAR:
- empathy_contact_set_avatar (contact, g_value_get_boxed (value));
- break;
case PROP_PRESENCE:
empathy_contact_set_presence (contact, g_value_get_uint (value));
break;
};
}
-EmpathyContact *
+static EmpathyContact *
empathy_contact_new (TpContact *tp_contact)
{
g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
NULL);
if (!EMP_STR_EMPTY (tpl_entity_get_avatar_token (tpl_entity)))
- empathy_contact_load_avatar_cache (retval,
+ contact_load_avatar_cache (retval,
tpl_entity_get_avatar_token (tpl_entity));
return retval;
}
-EmpathyContact *
-empathy_contact_new_for_log (TpAccount *account,
- const gchar *id,
- const gchar *alias,
- gboolean is_user)
-{
- g_return_val_if_fail (id != NULL, NULL);
- g_assert (account != NULL);
-
- return g_object_new (EMPATHY_TYPE_CONTACT,
- "account", account,
- "id", id,
- "alias", alias,
- "is-user", is_user,
- NULL);
-}
-
TpContact *
empathy_contact_get_tp_contact (EmpathyContact *contact)
{
return priv->id;
}
-void
-empathy_contact_set_id (EmpathyContact *contact,
- const gchar *id)
-{
- EmpathyContactPriv *priv;
-
- g_return_if_fail (EMPATHY_IS_CONTACT (contact));
- g_return_if_fail (id != NULL);
-
- priv = GET_PRIV (contact);
-
- /* We temporally ref the contact because it could be destroyed
- * during the signal emition */
- g_object_ref (contact);
- if (tp_strdiff (id, priv->id))
- {
- g_free (priv->id);
- priv->id = g_strdup (id);
-
- g_object_notify (G_OBJECT (contact), "id");
- if (EMP_STR_EMPTY (priv->alias))
- g_object_notify (G_OBJECT (contact), "alias");
- }
-
- g_object_unref (contact);
-}
-
const gchar *
empathy_contact_get_alias (EmpathyContact *contact)
{
/* Set the alias on the persona if possible */
persona = empathy_contact_get_persona (contact);
- if (persona != NULL && FOLKS_IS_ALIAS (persona))
+ if (persona != NULL && FOLKS_IS_ALIASABLE (persona))
{
DEBUG ("Setting alias for contact %s to %s",
empathy_contact_get_id (contact), alias);
- folks_alias_set_alias (FOLKS_ALIAS (persona), alias);
+ folks_aliasable_set_alias (FOLKS_ALIASABLE (persona), alias);
}
if (tp_strdiff (alias, priv->alias))
g_object_unref (contact);
}
+static void
+groups_change_group_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ FolksGroupable *groupable = FOLKS_GROUPABLE (source);
+ GError *error = NULL;
+
+ folks_groupable_change_group_finish (groupable, result, &error);
+ if (error != NULL)
+ {
+ g_warning ("failed to change group: %s", error->message);
+ g_clear_error (&error);
+ }
+}
+
void
empathy_contact_change_group (EmpathyContact *contact, const gchar *group,
gboolean is_member)
persona = empathy_contact_get_persona (contact);
if (persona != NULL)
{
- if (FOLKS_IS_GROUPS (persona))
- folks_groups_change_group (FOLKS_GROUPS (persona), group, is_member);
+ if (FOLKS_IS_GROUPABLE (persona))
+ folks_groupable_change_group (FOLKS_GROUPABLE (persona), group, is_member,
+ groups_change_group_cb, contact);
return;
}
return priv->avatar;
}
-void
-empathy_contact_set_avatar (EmpathyContact *contact,
- EmpathyAvatar *avatar)
+static void
+contact_set_avatar (EmpathyContact *contact,
+ EmpathyAvatar *avatar)
{
EmpathyContactPriv *priv;
if (tp_contact == priv->tp_contact)
{
/* Found the right persona */
- priv->persona = g_object_ref (persona);
+ empathy_contact_set_persona (contact,
+ (FolksPersona *) persona);
goto finished;
}
}
EmpathyContactPriv *priv;
g_return_if_fail (EMPATHY_IS_CONTACT (contact));
- g_return_if_fail (FOLKS_IS_PERSONA (persona));
+ g_return_if_fail (TPF_IS_PERSONA (persona));
priv = GET_PRIV (contact);
return;
if (priv->persona != NULL)
- g_object_unref (priv->persona);
+ {
+ g_signal_handlers_disconnect_by_func (priv->persona,
+ folks_persona_notify_cb, contact);
+ g_object_unref (priv->persona);
+ }
priv->persona = g_object_ref (persona);
+ g_signal_connect (priv->persona, "notify",
+ G_CALLBACK (folks_persona_notify_cb), contact);
+
g_object_notify (G_OBJECT (contact), "persona");
/* Set the persona's alias, since ours could've been set using
* empathy_contact_set_alias() before we had a persona; this happens when
* adding a contact. */
- empathy_contact_set_alias (contact, priv->alias);
+ if (priv->alias != NULL)
+ empathy_contact_set_alias (contact, priv->alias);
/* Set the persona's groups */
if (priv->groups != NULL)
{
- if (FOLKS_IS_GROUPS (persona))
- folks_groups_set_groups (FOLKS_GROUPS (persona), priv->groups);
-
+ folks_groupable_set_groups (FOLKS_GROUPABLE (persona), priv->groups);
g_hash_table_destroy (priv->groups);
priv->groups = NULL;
}
return priv->presence;
}
-void
-empathy_contact_set_presence (EmpathyContact *contact,
- TpConnectionPresenceType presence)
-{
- EmpathyContactPriv *priv;
- TpConnectionPresenceType old_presence;
-
- g_return_if_fail (EMPATHY_IS_CONTACT (contact));
-
- priv = GET_PRIV (contact);
-
- if (presence == priv->presence)
- return;
-
- old_presence = priv->presence;
- priv->presence = presence;
-
- g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence, old_presence);
-
- g_object_notify (G_OBJECT (contact), "presence");
-}
-
const gchar *
empathy_contact_get_presence_message (EmpathyContact *contact)
{
priv = GET_PRIV (contact);
- if (priv->tp_contact != NULL)
- return tp_contact_get_presence_message (priv->tp_contact);
-
- return priv->presence_message;
-}
-
-void
-empathy_contact_set_presence_message (EmpathyContact *contact,
- const gchar *message)
-{
- EmpathyContactPriv *priv = GET_PRIV (contact);
-
- g_return_if_fail (EMPATHY_IS_CONTACT (contact));
-
- if (!tp_strdiff (message, priv->presence_message))
- return;
-
- g_free (priv->presence_message);
- priv->presence_message = g_strdup (message);
+ if (priv->persona != NULL)
+ return folks_presence_get_presence_message (FOLKS_PRESENCE (priv->persona));
- g_object_notify (G_OBJECT (contact), "presence-message");
+ return NULL;
}
guint
return priv->handle;
}
-void
-empathy_contact_set_handle (EmpathyContact *contact,
- guint handle)
-{
- EmpathyContactPriv *priv;
-
- g_return_if_fail (EMPATHY_IS_CONTACT (contact));
-
- priv = GET_PRIV (contact);
-
- g_object_ref (contact);
- if (handle != priv->handle)
- {
- priv->handle = handle;
- g_object_notify (G_OBJECT (contact), "handle");
- }
- g_object_unref (contact);
-}
-
EmpathyCapabilities
empathy_contact_get_capabilities (EmpathyContact *contact)
{
return priv->capabilities;
}
-void
-empathy_contact_set_capabilities (EmpathyContact *contact,
- EmpathyCapabilities capabilities)
-{
- EmpathyContactPriv *priv;
-
- g_return_if_fail (EMPATHY_IS_CONTACT (contact));
-
- priv = GET_PRIV (contact);
-
- if (priv->capabilities == capabilities)
- return;
-
- priv->capabilities = capabilities;
-
- g_object_notify (G_OBJECT (contact), "capabilities");
-}
-
gboolean
empathy_contact_is_user (EmpathyContact *contact)
{
case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
case TP_CONNECTION_PRESENCE_TYPE_ERROR:
return FALSE;
+ /* Contacts without presence are considered online so we can display IRC
+ * contacts in rooms. */
+ case TP_CONNECTION_PRESENCE_TYPE_UNSET:
+ case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
+ case TP_CONNECTION_PRESENCE_TYPE_AWAY:
+ case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
+ case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
+ case TP_CONNECTION_PRESENCE_TYPE_BUSY:
default:
return TRUE;
}
return priv->capabilities & EMPATHY_CAPABILITIES_RFB_STREAM_TUBE;
}
+static gboolean
+contact_has_log (EmpathyContact *contact)
+{
+ TplLogManager *manager;
+ gboolean have_log;
+
+ manager = tpl_log_manager_dup_singleton ();
+ have_log = tpl_log_manager_exists (manager,
+ empathy_contact_get_account (contact), empathy_contact_get_id (contact),
+ FALSE);
+ g_object_unref (manager);
+
+ return have_log;
+}
+
+gboolean
+empathy_contact_can_do_action (EmpathyContact *self,
+ EmpathyActionType action_type)
+{
+ gboolean sensitivity = FALSE;
+
+ switch (action_type)
+ {
+ case EMPATHY_ACTION_CHAT:
+ sensitivity = TRUE;
+ break;
+ case EMPATHY_ACTION_AUDIO_CALL:
+ sensitivity = empathy_contact_can_voip_audio (self);
+ break;
+ case EMPATHY_ACTION_VIDEO_CALL:
+ sensitivity = empathy_contact_can_voip_video (self);
+ break;
+ case EMPATHY_ACTION_VIEW_LOGS:
+ sensitivity = contact_has_log (self);
+ break;
+ case EMPATHY_ACTION_SEND_FILE:
+ sensitivity = empathy_contact_can_send_files (self);
+ break;
+ case EMPATHY_ACTION_SHARE_MY_DESKTOP:
+ sensitivity = empathy_contact_can_use_rfb_stream_tube (self);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return (sensitivity ? TRUE : FALSE);
+}
+
static gchar *
contact_get_avatar_filename (EmpathyContact *contact,
const gchar *token)
return avatar_file;
}
-gboolean
-empathy_contact_load_avatar_cache (EmpathyContact *contact,
- const gchar *token)
+static gboolean
+contact_load_avatar_cache (EmpathyContact *contact,
+ const gchar *token)
{
EmpathyAvatar *avatar = NULL;
gchar *filename;
if (data)
{
DEBUG ("Avatar loaded from %s", filename);
- avatar = empathy_avatar_new ((guchar *) data, len, NULL, g_strdup (token),
- filename);
- empathy_contact_set_avatar (contact, avatar);
+ avatar = empathy_avatar_new ((guchar *) data, len, NULL, filename);
+ contact_set_avatar (contact, avatar);
empathy_avatar_unref (avatar);
}
else
* @data: the avatar data
* @len: the size of avatar data
* @format: the mime type of the avatar image
- * @token: the token of the avatar
* @filename: the filename where the avatar is stored in cache
*
* Create a #EmpathyAvatar from the provided data. This function takes the
- * ownership of @data, @format, @token and @filename.
+ * ownership of @data, @format and @filename.
*
* Returns: a new #EmpathyAvatar
*/
empathy_avatar_new (guchar *data,
gsize len,
gchar *format,
- gchar *token,
gchar *filename)
{
EmpathyAvatar *avatar;
avatar->data = data;
avatar->len = len;
avatar->format = format;
- avatar->token = token;
avatar->filename = filename;
avatar->refcount = 1;
{
g_free (avatar->data);
g_free (avatar->format);
- g_free (avatar->token);
g_free (avatar->filename);
g_slice_free (EmpathyAvatar, avatar);
}
g_hash_table_unref (priv->location);
priv->location = g_hash_table_ref (location);
-#if HAVE_GEOCLUE
+#ifdef HAVE_GEOCLUE
update_geocode (contact);
#endif
g_object_notify (G_OBJECT (contact), "location");
}
+const gchar * const *
+empathy_contact_get_client_types (EmpathyContact *contact)
+{
+ EmpathyContactPriv *priv;
+
+ g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
+
+ priv = GET_PRIV (contact);
+
+ return (const gchar * const *) priv->client_types;
+}
+
+static void
+contact_set_client_types (EmpathyContact *contact,
+ const gchar * const *client_types)
+{
+ EmpathyContactPriv *priv = GET_PRIV (contact);
+
+ if (priv->client_types != NULL)
+ g_strfreev (priv->client_types);
+
+ priv->client_types = g_strdupv ((gchar **) client_types);
+ g_object_notify (G_OBJECT (contact), "client-types");
+}
+
/**
* empathy_contact_equal:
* @contact1: an #EmpathyContact
return FALSE;
}
-#if HAVE_GEOCLUE
+#ifdef HAVE_GEOCLUE
#define GEOCODE_SERVICE "org.freedesktop.Geoclue.Providers.Yahoo"
#define GEOCODE_PATH "/org/freedesktop/Geoclue/Providers/Yahoo"
{
EmpathyContactPriv *priv = GET_PRIV (contact);
const gchar *mime;
- const gchar *token;
GFile *file;
- token = tp_contact_get_avatar_token (priv->tp_contact);
mime = tp_contact_get_avatar_mime_type (priv->tp_contact);
file = tp_contact_get_avatar_file (priv->tp_contact);
gsize len;
g_file_load_contents (file, NULL, &data, &len, NULL, NULL);
- avatar = empathy_avatar_new ((guchar *) data, len, g_strdup (mime), g_strdup (token),
+ avatar = empathy_avatar_new ((guchar *) data, len, g_strdup (mime),
g_file_get_path (file));
- empathy_contact_set_avatar (contact, avatar);
+ contact_set_avatar (contact, avatar);
empathy_avatar_unref (avatar);
}
else
{
- empathy_contact_set_avatar (contact, NULL);
+ contact_set_avatar (contact, NULL);
}
}
return contact;
}
+static int
+presence_cmp_func (EmpathyContact *a,
+ EmpathyContact *b)
+{
+ FolksPresence *presence_a, *presence_b;
+
+ presence_a = FOLKS_PRESENCE (empathy_contact_get_persona (a));
+ presence_b = FOLKS_PRESENCE (empathy_contact_get_persona (b));
+
+ /* We negate the result because we're sorting in reverse order (i.e. such that
+ * the Personas with the highest presence are at the beginning of the list. */
+ return -folks_presence_typecmp (folks_presence_get_presence_type (presence_a),
+ folks_presence_get_presence_type (presence_b));
+}
+
+static gint
+voip_cmp_func (EmpathyContact *a,
+ EmpathyContact *b)
+{
+ gboolean has_audio_a, has_audio_b;
+ gboolean has_video_a, has_video_b;
+
+ has_audio_a = empathy_contact_can_voip_audio (a);
+ has_audio_b = empathy_contact_can_voip_audio (b);
+ has_video_a = empathy_contact_can_voip_video (a);
+ has_video_b = empathy_contact_can_voip_video (b);
+
+ /* First check video */
+ if (has_video_a == has_video_b)
+ {
+ /* Use audio to to break tie */
+ if (has_audio_a == has_audio_b)
+ return 0;
+ else if (has_audio_a)
+ return -1;
+ else
+ return 1;
+ }
+ else if (has_video_a)
+ return -1;
+ else
+ return 1;
+}
+
+static gint
+ft_cmp_func (EmpathyContact *a,
+ EmpathyContact *b)
+{
+ gboolean can_send_files_a, can_send_files_b;
+
+ can_send_files_a = empathy_contact_can_send_files (a);
+ can_send_files_b = empathy_contact_can_send_files (b);
+
+ if (can_send_files_a == can_send_files_b)
+ return 0;
+ else if (can_send_files_a)
+ return -1;
+ else
+ return 1;
+}
+
+static gint
+rfb_stream_tube_cmp_func (EmpathyContact *a,
+ EmpathyContact *b)
+{
+ gboolean rfb_a, rfb_b;
+
+ rfb_a = empathy_contact_can_use_rfb_stream_tube (a);
+ rfb_b = empathy_contact_can_use_rfb_stream_tube (b);
+
+ if (rfb_a == rfb_b)
+ return 0;
+ else if (rfb_a)
+ return -1;
+ else
+ return 1;
+}
+
+/* Sort by presence as with presence_cmp_func(), but if the two contacts have
+ * the same presence, prefer the one which can do both audio *and* video calls,
+ * over the one which can only do one of the two. */
+static int
+voip_sort_func (EmpathyContact *a, EmpathyContact *b)
+{
+ gint presence_sort = presence_cmp_func (a, b);
+
+ if (presence_sort != 0)
+ return presence_sort;
+
+ return voip_cmp_func (a, b);
+}
+
+/* Sort by presence as with presence_cmp_func() and then break ties using the
+ * most "capable" individual. So users will be able to pick more actions on
+ * the contact in the "Contact" menu of the chat window. */
+static gint
+chat_sort_func (EmpathyContact *a,
+ EmpathyContact *b)
+{
+ gint result;
+
+ result = presence_cmp_func (a, b);
+ if (result != 0)
+ return result;
+
+ /* Prefer individual supporting file transfer */
+ result = ft_cmp_func (a, b);
+ if (result != 0)
+ return result;
+
+ /* Check audio/video capabilities */
+ result = voip_cmp_func (a, b);
+ if (result != 0)
+ return result;
+
+ /* Check 'Share my destop' feature */
+ return rfb_stream_tube_cmp_func (a, b);
+}
+
+static GCompareFunc
+get_sort_func_for_action (EmpathyActionType action_type)
+{
+ switch (action_type)
+ {
+ case EMPATHY_ACTION_AUDIO_CALL:
+ case EMPATHY_ACTION_VIDEO_CALL:
+ return (GCompareFunc) voip_sort_func;
+ case EMPATHY_ACTION_CHAT:
+ return (GCompareFunc) chat_sort_func;
+ case EMPATHY_ACTION_VIEW_LOGS:
+ case EMPATHY_ACTION_SEND_FILE:
+ case EMPATHY_ACTION_SHARE_MY_DESKTOP:
+ default:
+ return (GCompareFunc) presence_cmp_func;
+ }
+}
+
+/**
+ * empathy_contact_dup_best_for_action:
+ * @individual: a #FolksIndividual
+ * @action_type: the type of action to be performed on the contact
+ *
+ * Chooses a #FolksPersona from the given @individual which is best-suited for
+ * the given @action_type. "Best-suited" is determined by choosing the persona
+ * with the highest presence out of all the personas which can perform the given
+ * @action_type (e.g. are capable of video calling).
+ *
+ * Return value: an #EmpathyContact for the best persona, or %NULL;
+ * unref with g_object_unref()
+ */
+EmpathyContact *
+empathy_contact_dup_best_for_action (FolksIndividual *individual,
+ EmpathyActionType action_type)
+{
+ GList *personas, *contacts, *l;
+ EmpathyContact *best_contact = NULL;
+
+ /* Build a list of EmpathyContacts that we can sort */
+ personas = folks_individual_get_personas (individual);
+ contacts = NULL;
+
+ for (l = personas; l != NULL; l = l->next)
+ {
+ TpContact *tp_contact;
+ EmpathyContact *contact;
+
+ if (!TPF_IS_PERSONA (l->data))
+ continue;
+
+ tp_contact = tpf_persona_get_contact (TPF_PERSONA (l->data));
+ contact = empathy_contact_dup_from_tp_contact (tp_contact);
+ empathy_contact_set_persona (contact, FOLKS_PERSONA (l->data));
+
+ /* Only choose the contact if they're actually capable of the specified
+ * action. */
+ if (!empathy_contact_can_do_action (contact, action_type))
+ {
+ g_object_unref (contact);
+ continue;
+ }
+
+ contacts = g_list_prepend (contacts, contact);
+ }
+
+ /* Sort the contacts by some heuristic based on the action type, then take
+ * the top contact. */
+ if (contacts != NULL)
+ {
+ contacts = g_list_sort (contacts, get_sort_func_for_action (action_type));
+ best_contact = g_object_ref (contacts->data);
+ }
+
+ g_list_foreach (contacts, (GFunc) g_object_unref, NULL);
+ g_list_free (contacts);
+
+ return best_contact;
+}