1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
3 * Copyright (C) 2007-2009 Collabora Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * Authors: Xavier Claessens <xclaesse@gmail.com>
23 #include "empathy-contact.h"
25 #include <tp-account-widgets/tpaw-utils.h>
28 #include <geocode-glib/geocode-glib.h>
31 #include "empathy-location.h"
32 #include "empathy-utils.h"
33 #include "empathy-enum-types.h"
35 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
36 #include "empathy-debug.h"
38 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContact)
40 TpContact *tp_contact;
42 FolksPersona *persona;
46 EmpathyAvatar *avatar;
47 TpConnectionPresenceType presence;
49 EmpathyCapabilities capabilities;
51 /* Location is composed of string keys and GValues.
52 * Example: a "city" key would have "Helsinki" as string GValue,
53 * a "latitude" would have 65.0 as double GValue.
55 * This is a super set of the location stored in TpContact as we can try add
56 * more fields by searching the address using geoclue.
63 static void contact_finalize (GObject *object);
64 static void contact_get_property (GObject *object, guint param_id,
65 GValue *value, GParamSpec *pspec);
66 static void contact_set_property (GObject *object, guint param_id,
67 const GValue *value, GParamSpec *pspec);
70 static void update_geocode (EmpathyContact *contact);
73 static void empathy_contact_set_location (EmpathyContact *contact,
74 GHashTable *location);
76 static void contact_set_client_types (EmpathyContact *contact,
77 const gchar * const *types);
79 static void set_capabilities_from_tp_caps (EmpathyContact *self,
80 TpCapabilities *caps);
82 static void contact_set_avatar (EmpathyContact *contact,
83 EmpathyAvatar *avatar);
84 static void contact_set_avatar_from_tp_contact (EmpathyContact *contact);
85 static gboolean contact_load_avatar_cache (EmpathyContact *contact,
88 G_DEFINE_TYPE (EmpathyContact, empathy_contact, G_TYPE_OBJECT);
101 PROP_PRESENCE_MESSAGE,
114 static guint signals[LAST_SIGNAL];
116 /* TpContact* -> EmpathyContact*, both borrowed ref */
117 static GHashTable *contacts_table = NULL;
120 tp_contact_notify_cb (TpContact *tp_contact,
124 EmpathyContactPriv *priv = GET_PRIV (contact);
126 /* Forward property notifications */
127 if (!tp_strdiff (param->name, "alias"))
128 g_object_notify (contact, "alias");
129 else if (!tp_strdiff (param->name, "presence-type")) {
130 TpConnectionPresenceType presence;
132 presence = empathy_contact_get_presence (EMPATHY_CONTACT (contact));
133 g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence,
135 priv->presence = presence;
136 g_object_notify (contact, "presence");
138 else if (!tp_strdiff (param->name, "identifier"))
139 g_object_notify (contact, "id");
140 else if (!tp_strdiff (param->name, "handle"))
141 g_object_notify (contact, "handle");
142 else if (!tp_strdiff (param->name, "location"))
144 GHashTable *location;
146 location = tp_contact_get_location (tp_contact);
147 /* This will start a geoclue search to find the address if needed */
148 empathy_contact_set_location (EMPATHY_CONTACT (contact), location);
150 else if (!tp_strdiff (param->name, "capabilities"))
152 set_capabilities_from_tp_caps (EMPATHY_CONTACT (contact),
153 tp_contact_get_capabilities (tp_contact));
155 else if (!tp_strdiff (param->name, "avatar-file"))
157 contact_set_avatar_from_tp_contact (EMPATHY_CONTACT (contact));
159 else if (!tp_strdiff (param->name, "client-types"))
161 contact_set_client_types (EMPATHY_CONTACT (contact),
162 tp_contact_get_client_types (tp_contact));
167 folks_persona_notify_cb (FolksPersona *folks_persona,
171 if (!tp_strdiff (param->name, "presence-message"))
172 g_object_notify (contact, "presence-message");
176 contact_dispose (GObject *object)
178 EmpathyContactPriv *priv = GET_PRIV (object);
180 if (priv->tp_contact != NULL)
182 g_signal_handlers_disconnect_by_func (priv->tp_contact,
183 tp_contact_notify_cb, object);
185 tp_clear_object (&priv->tp_contact);
188 g_object_unref (priv->account);
189 priv->account = NULL;
193 g_signal_handlers_disconnect_by_func (priv->persona,
194 folks_persona_notify_cb, object);
195 g_object_unref (priv->persona);
197 priv->persona = NULL;
199 if (priv->avatar != NULL)
201 empathy_avatar_unref (priv->avatar);
205 if (priv->location != NULL)
207 g_hash_table_unref (priv->location);
208 priv->location = NULL;
211 G_OBJECT_CLASS (empathy_contact_parent_class)->dispose (object);
215 contact_constructed (GObject *object)
217 EmpathyContact *contact = (EmpathyContact *) object;
218 EmpathyContactPriv *priv = GET_PRIV (contact);
219 GHashTable *location;
220 TpContact *self_contact;
221 const gchar * const *client_types;
223 if (priv->tp_contact == NULL)
226 priv->presence = empathy_contact_get_presence (contact);
228 location = tp_contact_get_location (priv->tp_contact);
229 if (location != NULL)
230 empathy_contact_set_location (contact, location);
232 client_types = tp_contact_get_client_types (priv->tp_contact);
233 if (client_types != NULL)
234 contact_set_client_types (contact, client_types);
236 set_capabilities_from_tp_caps (contact,
237 tp_contact_get_capabilities (priv->tp_contact));
239 contact_set_avatar_from_tp_contact (contact);
241 /* Set is-user property. Note that it could still be the handle is
242 * different from the connection's self handle, in the case the handle
243 * comes from a group interface. */
244 self_contact = tp_connection_get_self_contact (
245 tp_contact_get_connection (priv->tp_contact));
246 empathy_contact_set_is_user (contact, self_contact == priv->tp_contact);
248 g_signal_connect (priv->tp_contact, "notify",
249 G_CALLBACK (tp_contact_notify_cb), contact);
253 empathy_contact_class_init (EmpathyContactClass *class)
255 GObjectClass *object_class;
257 object_class = G_OBJECT_CLASS (class);
259 object_class->finalize = contact_finalize;
260 object_class->dispose = contact_dispose;
261 object_class->get_property = contact_get_property;
262 object_class->set_property = contact_set_property;
263 object_class->constructed = contact_constructed;
265 g_object_class_install_property (object_class,
267 g_param_spec_object ("tp-contact",
269 "The TpContact associated with the contact",
271 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
273 g_object_class_install_property (object_class,
275 g_param_spec_object ("account",
277 "The account associated with the contact",
279 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
281 g_object_class_install_property (object_class,
283 g_param_spec_object ("persona",
285 "The FolksPersona associated with the contact",
287 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
289 g_object_class_install_property (object_class,
291 g_param_spec_string ("id",
293 "String identifying contact",
295 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
297 g_object_class_install_property (object_class,
299 g_param_spec_string ("alias",
301 "An alias for the contact",
303 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
305 g_object_class_install_property (object_class,
307 g_param_spec_string ("logged-alias",
309 "The alias the user had when a message was logged, "
310 "only set when using empathy_contact_from_tpl_contact()",
312 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
314 g_object_class_install_property (object_class,
316 g_param_spec_boxed ("avatar",
320 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
322 g_object_class_install_property (object_class,
324 g_param_spec_uint ("presence",
326 "Presence of contact",
327 TP_CONNECTION_PRESENCE_TYPE_UNSET,
328 TP_NUM_CONNECTION_PRESENCE_TYPES,
329 TP_CONNECTION_PRESENCE_TYPE_UNSET,
330 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
332 g_object_class_install_property (object_class,
333 PROP_PRESENCE_MESSAGE,
334 g_param_spec_string ("presence-message",
335 "Contact presence message",
336 "Presence message of contact",
338 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
340 g_object_class_install_property (object_class,
342 g_param_spec_uint ("handle",
344 "The handle of the contact",
348 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
350 g_object_class_install_property (object_class,
352 g_param_spec_flags ("capabilities",
353 "Contact Capabilities",
354 "Capabilities of the contact",
355 EMPATHY_TYPE_CAPABILITIES,
356 EMPATHY_CAPABILITIES_UNKNOWN,
357 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
359 g_object_class_install_property (object_class,
361 g_param_spec_boolean ("is-user",
363 "Is contact the user",
365 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
368 g_object_class_install_property (object_class,
370 g_param_spec_boxed ("location",
372 "Physical location of the contact",
374 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
376 g_object_class_install_property (object_class,
378 g_param_spec_boxed ("client-types",
379 "Contact client types",
380 "Client types of the contact",
382 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
384 signals[PRESENCE_CHANGED] =
385 g_signal_new ("presence-changed",
386 G_TYPE_FROM_CLASS (class),
390 g_cclosure_marshal_generic,
395 g_type_class_add_private (object_class, sizeof (EmpathyContactPriv));
399 empathy_contact_init (EmpathyContact *contact)
401 EmpathyContactPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (contact,
402 EMPATHY_TYPE_CONTACT, EmpathyContactPriv);
404 contact->priv = priv;
406 priv->location = NULL;
407 priv->client_types = NULL;
412 contact_finalize (GObject *object)
414 EmpathyContactPriv *priv;
416 priv = GET_PRIV (object);
418 DEBUG ("finalize: %p", object);
420 g_clear_object (&priv->groups);
421 g_free (priv->alias);
422 g_free (priv->logged_alias);
424 g_strfreev (priv->client_types);
426 G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object);
430 empathy_contact_set_capabilities (EmpathyContact *contact,
431 EmpathyCapabilities capabilities)
433 EmpathyContactPriv *priv;
435 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
437 priv = GET_PRIV (contact);
439 if (priv->capabilities == capabilities)
442 priv->capabilities = capabilities;
444 g_object_notify (G_OBJECT (contact), "capabilities");
448 empathy_contact_set_id (EmpathyContact *contact,
451 EmpathyContactPriv *priv;
453 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
454 g_return_if_fail (id != NULL);
456 priv = GET_PRIV (contact);
458 /* We temporally ref the contact because it could be destroyed
459 * during the signal emition */
460 g_object_ref (contact);
461 if (tp_strdiff (id, priv->id))
464 priv->id = g_strdup (id);
466 g_object_notify (G_OBJECT (contact), "id");
467 if (TPAW_STR_EMPTY (priv->alias))
468 g_object_notify (G_OBJECT (contact), "alias");
471 g_object_unref (contact);
475 empathy_contact_set_presence (EmpathyContact *contact,
476 TpConnectionPresenceType presence)
478 EmpathyContactPriv *priv;
479 TpConnectionPresenceType old_presence;
481 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
483 priv = GET_PRIV (contact);
485 if (presence == priv->presence)
488 old_presence = priv->presence;
489 priv->presence = presence;
491 g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence, old_presence);
493 g_object_notify (G_OBJECT (contact), "presence");
497 empathy_contact_set_presence_message (EmpathyContact *contact,
498 const gchar *message)
500 EmpathyContactPriv *priv = GET_PRIV (contact);
502 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
504 if (priv->persona != NULL)
506 folks_presence_details_set_presence_message (
507 FOLKS_PRESENCE_DETAILS (priv->persona), message);
512 empathy_contact_set_handle (EmpathyContact *contact,
515 EmpathyContactPriv *priv;
517 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
519 priv = GET_PRIV (contact);
521 g_object_ref (contact);
522 if (handle != priv->handle)
524 priv->handle = handle;
525 g_object_notify (G_OBJECT (contact), "handle");
527 g_object_unref (contact);
531 contact_get_property (GObject *object,
536 EmpathyContact *contact = EMPATHY_CONTACT (object);
540 case PROP_TP_CONTACT:
541 g_value_set_object (value, empathy_contact_get_tp_contact (contact));
544 g_value_set_object (value, empathy_contact_get_account (contact));
547 g_value_set_object (value, empathy_contact_get_persona (contact));
550 g_value_set_string (value, empathy_contact_get_id (contact));
553 g_value_set_string (value, empathy_contact_get_alias (contact));
555 case PROP_LOGGED_ALIAS:
556 g_value_set_string (value, empathy_contact_get_logged_alias (contact));
559 g_value_set_boxed (value, empathy_contact_get_avatar (contact));
562 g_value_set_uint (value, empathy_contact_get_presence (contact));
564 case PROP_PRESENCE_MESSAGE:
565 g_value_set_string (value, empathy_contact_get_presence_message (contact));
568 g_value_set_uint (value, empathy_contact_get_handle (contact));
570 case PROP_CAPABILITIES:
571 g_value_set_flags (value, empathy_contact_get_capabilities (contact));
574 g_value_set_boolean (value, empathy_contact_is_user (contact));
577 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
583 contact_set_property (GObject *object,
588 EmpathyContact *contact = EMPATHY_CONTACT (object);
589 EmpathyContactPriv *priv = GET_PRIV (object);
593 case PROP_TP_CONTACT:
594 priv->tp_contact = g_value_dup_object (value);
597 g_assert (priv->account == NULL);
598 priv->account = g_value_dup_object (value);
601 empathy_contact_set_persona (contact, g_value_get_object (value));
604 empathy_contact_set_id (contact, g_value_get_string (value));
607 empathy_contact_set_alias (contact, g_value_get_string (value));
609 case PROP_LOGGED_ALIAS:
610 g_assert (priv->logged_alias == NULL);
611 priv->logged_alias = g_value_dup_string (value);
614 empathy_contact_set_presence (contact, g_value_get_uint (value));
616 case PROP_PRESENCE_MESSAGE:
617 empathy_contact_set_presence_message (contact, g_value_get_string (value));
620 empathy_contact_set_handle (contact, g_value_get_uint (value));
622 case PROP_CAPABILITIES:
623 empathy_contact_set_capabilities (contact, g_value_get_flags (value));
626 empathy_contact_set_is_user (contact, g_value_get_boolean (value));
629 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
635 remove_tp_contact (gpointer data,
638 g_hash_table_remove (contacts_table, data);
641 static EmpathyContact *
642 empathy_contact_new (TpContact *tp_contact)
644 EmpathyContact *retval;
646 g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
648 retval = g_object_new (EMPATHY_TYPE_CONTACT,
649 "tp-contact", tp_contact,
652 g_object_weak_ref (G_OBJECT (retval), remove_tp_contact, tp_contact);
664 contact_is_tpl_entity (gpointer key,
668 EmpathyContact *contact = value;
669 FindContactData *data = user_data;
670 TpAccount *account = empathy_contact_get_account (contact);
671 const gchar *path = NULL;
674 path = tp_proxy_get_object_path (account);
676 return !tp_strdiff (empathy_contact_get_id (contact),
677 tpl_entity_get_identifier (data->entity)) &&
678 !tp_strdiff (tp_proxy_get_object_path (data->account), path);
682 get_contacts_cb (GObject *source,
683 GAsyncResult *result,
686 TpWeakRef *wr = user_data;
687 EmpathyContactPriv *priv;
688 EmpathyContact *self;
690 self = tp_weak_ref_dup_object (wr);
694 priv = GET_PRIV (self);
696 g_return_if_fail (priv->tp_contact == NULL);
698 priv->tp_contact = tp_connection_dup_contact_by_id_finish (
699 TP_CONNECTION (source), result, NULL);
700 if (priv->tp_contact == NULL)
703 g_object_notify (G_OBJECT (self), "tp-contact");
705 /* Update capabilities now that we have a TpContact */
706 set_capabilities_from_tp_caps (self,
707 tp_contact_get_capabilities (priv->tp_contact));
710 g_clear_object (&self);
711 tp_weak_ref_destroy (wr);
715 empathy_contact_from_tpl_contact (TpAccount *account,
716 TplEntity *tpl_entity)
718 EmpathyContact *retval;
720 EmpathyContact *existing_contact = NULL;
722 g_return_val_if_fail (TPL_IS_ENTITY (tpl_entity), NULL);
724 if (contacts_table != NULL)
726 FindContactData data;
728 data.entity = tpl_entity;
729 data.account = account;
731 existing_contact = g_hash_table_find (contacts_table,
732 contact_is_tpl_entity, &data);
735 if (existing_contact != NULL)
737 retval = g_object_new (EMPATHY_TYPE_CONTACT,
738 "tp-contact", empathy_contact_get_tp_contact (existing_contact),
739 "logged-alias", tpl_entity_get_alias (tpl_entity),
747 is_user = (TPL_ENTITY_SELF == tpl_entity_get_entity_type (tpl_entity));
749 id = tpl_entity_get_identifier (tpl_entity);
751 retval = g_object_new (EMPATHY_TYPE_CONTACT,
753 "alias", tpl_entity_get_alias (tpl_entity),
758 /* Try to get a TpContact associated to have at least contact
759 * capabilities if possible. This is useful for CM supporting calling
760 * offline contacts for example. */
761 conn = tp_account_get_connection (account);
764 TpContactFeature features[] = { TP_CONTACT_FEATURE_CAPABILITIES };
765 conn = tp_account_get_connection (account);
767 tp_connection_dup_contact_by_id_async (conn, id,
768 G_N_ELEMENTS (features), features, get_contacts_cb,
769 tp_weak_ref_new (retval, NULL, NULL));
773 if (!TPAW_STR_EMPTY (tpl_entity_get_avatar_token (tpl_entity)))
774 contact_load_avatar_cache (retval,
775 tpl_entity_get_avatar_token (tpl_entity));
781 empathy_contact_get_tp_contact (EmpathyContact *contact)
783 EmpathyContactPriv *priv;
785 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
787 priv = GET_PRIV (contact);
789 return priv->tp_contact;
793 empathy_contact_get_id (EmpathyContact *contact)
795 EmpathyContactPriv *priv;
797 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
799 priv = GET_PRIV (contact);
801 if (priv->tp_contact != NULL)
802 return tp_contact_get_identifier (priv->tp_contact);
808 empathy_contact_get_alias (EmpathyContact *contact)
810 EmpathyContactPriv *priv;
811 const gchar *alias = NULL;
813 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
815 priv = GET_PRIV (contact);
817 if (!TPAW_STR_EMPTY (priv->alias))
819 else if (priv->tp_contact != NULL)
820 alias = tp_contact_get_alias (priv->tp_contact);
822 if (!TPAW_STR_EMPTY (alias))
825 return empathy_contact_get_id (contact);
829 empathy_contact_get_logged_alias (EmpathyContact *contact)
831 EmpathyContactPriv *priv;
833 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
835 priv = GET_PRIV (contact);
837 if (priv->logged_alias != NULL)
838 return priv->logged_alias;
840 return empathy_contact_get_alias (contact);
844 empathy_contact_set_alias (EmpathyContact *contact,
847 EmpathyContactPriv *priv;
848 FolksPersona *persona;
850 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
852 priv = GET_PRIV (contact);
854 g_object_ref (contact);
856 /* Set the alias on the persona if possible */
857 persona = empathy_contact_get_persona (contact);
858 if (persona != NULL && FOLKS_IS_ALIAS_DETAILS (persona))
860 DEBUG ("Setting alias for contact %s to %s",
861 empathy_contact_get_id (contact), alias);
863 folks_alias_details_set_alias (FOLKS_ALIAS_DETAILS (persona), alias);
866 if (tp_strdiff (alias, priv->alias))
868 g_free (priv->alias);
869 priv->alias = g_strdup (alias);
870 g_object_notify (G_OBJECT (contact), "alias");
873 g_object_unref (contact);
877 groups_change_group_cb (GObject *source,
878 GAsyncResult *result,
881 FolksGroupDetails *group_details = FOLKS_GROUP_DETAILS (source);
882 GError *error = NULL;
884 folks_group_details_change_group_finish (group_details, result, &error);
887 g_warning ("failed to change group: %s", error->message);
888 g_clear_error (&error);
893 empathy_contact_change_group (EmpathyContact *contact, const gchar *group,
896 EmpathyContactPriv *priv;
897 FolksPersona *persona;
899 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
900 g_return_if_fail (group != NULL);
902 priv = GET_PRIV (contact);
904 /* Normally pass through the changes to the persona */
905 persona = empathy_contact_get_persona (contact);
908 if (FOLKS_IS_GROUP_DETAILS (persona))
909 folks_group_details_change_group (FOLKS_GROUP_DETAILS (persona), group,
910 is_member, groups_change_group_cb, contact);
914 /* If the persona doesn't exist yet, we have to cache the changes until it
916 if (priv->groups == NULL)
918 priv->groups = gee_hash_set_new (G_TYPE_STRING, (GBoxedCopyFunc) g_strdup,
919 g_free, NULL, NULL, NULL, NULL, NULL, NULL);
922 gee_collection_add (GEE_COLLECTION (priv->groups), group);
926 empathy_contact_get_avatar (EmpathyContact *contact)
928 EmpathyContactPriv *priv;
930 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
932 priv = GET_PRIV (contact);
938 contact_set_avatar (EmpathyContact *contact,
939 EmpathyAvatar *avatar)
941 EmpathyContactPriv *priv;
943 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
945 priv = GET_PRIV (contact);
947 if (priv->avatar == avatar)
952 empathy_avatar_unref (priv->avatar);
957 priv->avatar = empathy_avatar_ref (avatar);
959 g_object_notify (G_OBJECT (contact), "avatar");
963 empathy_contact_get_account (EmpathyContact *contact)
965 EmpathyContactPriv *priv;
967 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
969 priv = GET_PRIV (contact);
971 if (priv->account == NULL && priv->tp_contact != NULL)
973 TpConnection *connection;
975 /* FIXME: This assume the account manager already exists */
976 connection = tp_contact_get_connection (priv->tp_contact);
978 g_object_ref (tp_connection_get_account (connection));
981 return priv->account;
985 empathy_contact_get_persona (EmpathyContact *contact)
987 EmpathyContactPriv *priv;
989 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
991 priv = GET_PRIV (contact);
993 if (priv->persona == NULL && priv->tp_contact != NULL)
997 persona = tpf_persona_dup_for_contact (priv->tp_contact);
1000 empathy_contact_set_persona (contact, (FolksPersona *) persona);
1001 g_object_unref (persona);
1005 return priv->persona;
1009 empathy_contact_set_persona (EmpathyContact *contact,
1010 FolksPersona *persona)
1012 EmpathyContactPriv *priv;
1014 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1015 g_return_if_fail (TPF_IS_PERSONA (persona));
1017 priv = GET_PRIV (contact);
1019 if (persona == priv->persona)
1022 if (priv->persona != NULL)
1024 g_signal_handlers_disconnect_by_func (priv->persona,
1025 folks_persona_notify_cb, contact);
1026 g_object_unref (priv->persona);
1028 priv->persona = g_object_ref (persona);
1030 g_signal_connect (priv->persona, "notify",
1031 G_CALLBACK (folks_persona_notify_cb), contact);
1033 g_object_notify (G_OBJECT (contact), "persona");
1035 /* Set the persona's alias, since ours could've been set using
1036 * empathy_contact_set_alias() before we had a persona; this happens when
1037 * adding a contact. */
1038 if (priv->alias != NULL)
1039 empathy_contact_set_alias (contact, priv->alias);
1041 /* Set the persona's groups */
1042 if (priv->groups != NULL)
1044 folks_group_details_set_groups (FOLKS_GROUP_DETAILS (persona),
1045 GEE_SET (priv->groups));
1046 g_object_unref (priv->groups);
1047 priv->groups = NULL;
1052 empathy_contact_get_connection (EmpathyContact *contact)
1054 EmpathyContactPriv *priv;
1056 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
1058 priv = GET_PRIV (contact);
1060 if (priv->tp_contact != NULL)
1061 return tp_contact_get_connection (priv->tp_contact);
1066 TpConnectionPresenceType
1067 empathy_contact_get_presence (EmpathyContact *contact)
1069 EmpathyContactPriv *priv;
1071 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact),
1072 TP_CONNECTION_PRESENCE_TYPE_UNSET);
1074 priv = GET_PRIV (contact);
1076 if (priv->tp_contact != NULL)
1077 return tp_contact_get_presence_type (priv->tp_contact);
1079 return priv->presence;
1083 empathy_contact_get_presence_message (EmpathyContact *contact)
1085 EmpathyContactPriv *priv;
1087 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
1089 priv = GET_PRIV (contact);
1091 if (priv->persona != NULL)
1092 return folks_presence_details_get_presence_message (
1093 FOLKS_PRESENCE_DETAILS (priv->persona));
1095 if (priv->tp_contact != NULL)
1096 return tp_contact_get_presence_message (priv->tp_contact);
1102 empathy_contact_get_handle (EmpathyContact *contact)
1104 EmpathyContactPriv *priv;
1106 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
1108 priv = GET_PRIV (contact);
1110 if (priv->tp_contact != NULL)
1111 return tp_contact_get_handle (priv->tp_contact);
1113 return priv->handle;
1117 empathy_contact_get_capabilities (EmpathyContact *contact)
1119 EmpathyContactPriv *priv;
1121 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
1123 priv = GET_PRIV (contact);
1125 return priv->capabilities;
1129 empathy_contact_is_user (EmpathyContact *contact)
1131 EmpathyContactPriv *priv;
1133 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1135 priv = GET_PRIV (contact);
1137 return priv->is_user;
1141 empathy_contact_set_is_user (EmpathyContact *contact,
1144 EmpathyContactPriv *priv;
1146 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1148 priv = GET_PRIV (contact);
1150 if (priv->is_user == is_user)
1153 priv->is_user = is_user;
1155 g_object_notify (G_OBJECT (contact), "is-user");
1159 empathy_contact_is_online (EmpathyContact *contact)
1161 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1163 switch (empathy_contact_get_presence (contact))
1165 case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
1166 case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
1167 case TP_CONNECTION_PRESENCE_TYPE_ERROR:
1169 /* Contacts without presence are considered online so we can display IRC
1170 * contacts in rooms. */
1171 case TP_CONNECTION_PRESENCE_TYPE_UNSET:
1172 case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
1173 case TP_CONNECTION_PRESENCE_TYPE_AWAY:
1174 case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
1175 case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
1176 case TP_CONNECTION_PRESENCE_TYPE_BUSY:
1183 empathy_contact_get_status (EmpathyContact *contact)
1185 const gchar *message;
1187 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), "");
1189 message = empathy_contact_get_presence_message (contact);
1190 if (!TPAW_STR_EMPTY (message))
1193 return empathy_presence_get_default_message (
1194 empathy_contact_get_presence (contact));
1198 empathy_contact_can_sms (EmpathyContact *contact)
1200 EmpathyContactPriv *priv;
1202 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1204 priv = GET_PRIV (contact);
1206 return priv->capabilities & EMPATHY_CAPABILITIES_SMS;
1210 empathy_contact_can_voip (EmpathyContact *contact)
1212 EmpathyContactPriv *priv;
1214 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1216 priv = GET_PRIV (contact);
1218 return priv->capabilities & (EMPATHY_CAPABILITIES_AUDIO |
1219 EMPATHY_CAPABILITIES_VIDEO);
1223 empathy_contact_can_voip_audio (EmpathyContact *contact)
1225 EmpathyContactPriv *priv;
1227 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1229 priv = GET_PRIV (contact);
1231 return priv->capabilities & EMPATHY_CAPABILITIES_AUDIO;
1235 empathy_contact_can_voip_video (EmpathyContact *contact)
1237 EmpathyContactPriv *priv;
1239 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1241 priv = GET_PRIV (contact);
1243 return priv->capabilities & EMPATHY_CAPABILITIES_VIDEO;
1247 empathy_contact_can_send_files (EmpathyContact *contact)
1249 EmpathyContactPriv *priv;
1251 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1253 priv = GET_PRIV (contact);
1255 return priv->capabilities & EMPATHY_CAPABILITIES_FT;
1259 empathy_contact_can_use_rfb_stream_tube (EmpathyContact *contact)
1261 EmpathyContactPriv *priv;
1263 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1265 priv = GET_PRIV (contact);
1267 return priv->capabilities & EMPATHY_CAPABILITIES_RFB_STREAM_TUBE;
1271 contact_has_log (EmpathyContact *contact)
1273 TplLogManager *manager;
1277 manager = tpl_log_manager_dup_singleton ();
1278 entity = tpl_entity_new (empathy_contact_get_id (contact),
1279 TPL_ENTITY_CONTACT, NULL, NULL);
1281 have_log = tpl_log_manager_exists (manager,
1282 empathy_contact_get_account (contact), entity, TPL_EVENT_MASK_TEXT);
1284 g_object_unref (entity);
1285 g_object_unref (manager);
1291 empathy_contact_can_do_action (EmpathyContact *self,
1292 EmpathyActionType action_type)
1294 gboolean sensitivity = FALSE;
1296 switch (action_type)
1298 case EMPATHY_ACTION_CHAT:
1301 case EMPATHY_ACTION_SMS:
1302 sensitivity = empathy_contact_can_sms (self);
1304 case EMPATHY_ACTION_AUDIO_CALL:
1305 sensitivity = empathy_contact_can_voip_audio (self);
1307 case EMPATHY_ACTION_VIDEO_CALL:
1308 sensitivity = empathy_contact_can_voip_video (self);
1310 case EMPATHY_ACTION_VIEW_LOGS:
1311 sensitivity = contact_has_log (self);
1313 case EMPATHY_ACTION_SEND_FILE:
1314 sensitivity = empathy_contact_can_send_files (self);
1316 case EMPATHY_ACTION_SHARE_MY_DESKTOP:
1317 sensitivity = empathy_contact_can_use_rfb_stream_tube (self);
1320 g_assert_not_reached ();
1323 return (sensitivity ? TRUE : FALSE);
1327 contact_get_avatar_filename (EmpathyContact *contact,
1333 gchar *token_escaped;
1335 if (TPAW_STR_EMPTY (empathy_contact_get_id (contact)))
1338 token_escaped = tp_escape_as_identifier (token);
1339 account = empathy_contact_get_account (contact);
1341 avatar_path = g_build_filename (g_get_user_cache_dir (),
1344 tp_account_get_cm_name (account),
1345 tp_account_get_protocol_name (account),
1347 g_mkdir_with_parents (avatar_path, 0700);
1349 avatar_file = g_build_filename (avatar_path, token_escaped, NULL);
1351 g_free (token_escaped);
1352 g_free (avatar_path);
1358 contact_load_avatar_cache (EmpathyContact *contact,
1361 EmpathyAvatar *avatar = NULL;
1365 GError *error = NULL;
1367 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1368 g_return_val_if_fail (!TPAW_STR_EMPTY (token), FALSE);
1370 /* Load the avatar from file if it exists */
1371 filename = contact_get_avatar_filename (contact, token);
1372 if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
1374 if (!g_file_get_contents (filename, &data, &len, &error))
1376 DEBUG ("Failed to load avatar from cache: %s",
1377 error ? error->message : "No error given");
1378 g_clear_error (&error);
1384 DEBUG ("Avatar loaded from %s", filename);
1385 avatar = empathy_avatar_new ((guchar *) data, len, NULL, filename);
1386 contact_set_avatar (contact, avatar);
1387 empathy_avatar_unref (avatar);
1393 return data != NULL;
1397 empathy_avatar_get_type (void)
1399 static GType type_id = 0;
1403 type_id = g_boxed_type_register_static ("EmpathyAvatar",
1404 (GBoxedCopyFunc) empathy_avatar_ref,
1405 (GBoxedFreeFunc) empathy_avatar_unref);
1412 * empathy_avatar_new:
1413 * @data: the avatar data
1414 * @len: the size of avatar data
1415 * @format: the mime type of the avatar image
1416 * @filename: the filename where the avatar is stored in cache
1418 * Create a #EmpathyAvatar from the provided data.
1420 * Returns: a new #EmpathyAvatar
1423 empathy_avatar_new (const guchar *data,
1425 const gchar *format,
1426 const gchar *filename)
1428 EmpathyAvatar *avatar;
1430 avatar = g_slice_new0 (EmpathyAvatar);
1431 avatar->data = g_memdup (data, len);
1433 avatar->format = g_strdup (format);
1434 avatar->filename = g_strdup (filename);
1435 avatar->refcount = 1;
1441 empathy_avatar_unref (EmpathyAvatar *avatar)
1443 g_return_if_fail (avatar != NULL);
1446 if (avatar->refcount == 0)
1448 g_free (avatar->data);
1449 g_free (avatar->format);
1450 g_free (avatar->filename);
1451 g_slice_free (EmpathyAvatar, avatar);
1456 empathy_avatar_ref (EmpathyAvatar *avatar)
1458 g_return_val_if_fail (avatar != NULL, NULL);
1466 * empathy_avatar_save_to_file:
1467 * @avatar: the avatar
1468 * @filename: name of a file to write avatar to
1469 * @error: return location for a GError, or NULL
1471 * Save the avatar to a file named filename
1473 * Returns: %TRUE on success, %FALSE if an error occurred
1476 empathy_avatar_save_to_file (EmpathyAvatar *self,
1477 const gchar *filename,
1480 return g_file_set_contents (filename, (const gchar *) self->data, self->len,
1485 * empathy_contact_get_location:
1486 * @contact: an #EmpathyContact
1488 * Returns the user's location if available. The keys are defined in
1489 * empathy-location.h. If the contact doesn't have location
1490 * information, the GHashTable will be empthy. Use #g_hash_table_unref when
1491 * you are done with the #GHashTable.
1493 * It is composed of string keys and GValues. Keys are
1494 * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
1495 * Example: a "city" key would have "Helsinki" as string GValue,
1496 * a "latitude" would have 65.0 as double GValue.
1498 * Returns: a #GHashTable of location values
1501 empathy_contact_get_location (EmpathyContact *contact)
1503 EmpathyContactPriv *priv;
1505 g_return_val_if_fail (EMPATHY_CONTACT (contact), NULL);
1507 priv = GET_PRIV (contact);
1509 return priv->location;
1513 * empathy_contact_set_location:
1514 * @contact: an #EmpathyContact
1515 * @location: a #GHashTable of the location
1517 * Sets the user's location based on the location #GHashTable passed.
1518 * It is composed of string keys and GValues. Keys are
1519 * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
1520 * Example: a "city" key would have "Helsinki" as string GValue,
1521 * a "latitude" would have 65.0 as double GValue.
1524 empathy_contact_set_location (EmpathyContact *contact,
1525 GHashTable *location)
1527 EmpathyContactPriv *priv;
1529 g_return_if_fail (EMPATHY_CONTACT (contact));
1530 g_return_if_fail (location != NULL);
1532 priv = GET_PRIV (contact);
1534 if (priv->location != NULL)
1535 g_hash_table_unref (priv->location);
1537 priv->location = g_hash_table_ref (location);
1539 update_geocode (contact);
1541 g_object_notify (G_OBJECT (contact), "location");
1544 const gchar * const *
1545 empathy_contact_get_client_types (EmpathyContact *contact)
1547 EmpathyContactPriv *priv;
1549 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
1551 priv = GET_PRIV (contact);
1553 return (const gchar * const *) priv->client_types;
1557 contact_set_client_types (EmpathyContact *contact,
1558 const gchar * const *client_types)
1560 EmpathyContactPriv *priv = GET_PRIV (contact);
1562 if (priv->client_types != NULL)
1563 g_strfreev (priv->client_types);
1565 priv->client_types = g_strdupv ((gchar **) client_types);
1566 g_object_notify (G_OBJECT (contact), "client-types");
1570 * empathy_contact_equal:
1571 * @contact1: an #EmpathyContact
1572 * @contact2: an #EmpathyContact
1574 * Returns FALSE if one of the contacts is NULL but the other is not.
1575 * Otherwise returns TRUE if both pointer are equal or if they bith
1576 * refer to the same id.
1577 * It's only necessary to call this function if your contact objects
1578 * come from logs where contacts are created dynamically and comparing
1579 * pointers is not enough.
1582 empathy_contact_equal (gconstpointer contact1,
1583 gconstpointer contact2)
1590 if ((contact1 == NULL) != (contact2 == NULL)) {
1593 if (contact1 == contact2) {
1596 c1 = EMPATHY_CONTACT (contact1);
1597 c2 = EMPATHY_CONTACT (contact2);
1598 id1 = empathy_contact_get_id (c1);
1599 id2 = empathy_contact_get_id (c2);
1600 if (!tp_strdiff (id1, id2)) {
1607 /* This callback is called by geocode-glib when it found a position
1608 * for the given address. A position is necessary for a contact
1609 * to show up on the map
1612 geocode_cb (GObject *source,
1613 GAsyncResult *result,
1616 EmpathyContact *contact = user_data;
1617 EmpathyContactPriv *priv = GET_PRIV (contact);
1618 GError *error = NULL;
1620 GeocodeLocation *loc;
1621 GHashTable *new_location;
1622 GHashTable *resolved = NULL;
1624 if (priv->location == NULL)
1627 res = geocode_forward_search_finish (GEOCODE_FORWARD (source), result,
1632 DEBUG ("Failed to resolve geocode: %s", error->message);
1633 g_error_free (error);
1639 new_location = tp_asv_new (
1640 EMPATHY_LOCATION_LAT, G_TYPE_DOUBLE, geocode_location_get_latitude (loc),
1641 EMPATHY_LOCATION_LON, G_TYPE_DOUBLE, geocode_location_get_longitude (loc),
1644 DEBUG ("\t - Latitude: %f", geocode_location_get_latitude (loc));
1645 DEBUG ("\t - Longitude: %f", geocode_location_get_longitude (loc));
1647 g_list_free_full (res, g_object_unref);
1649 /* Copy remaning fields. LAT and LON were not defined so we won't overwrite
1650 * the values we just set. */
1651 tp_g_hash_table_update (new_location, priv->location,
1652 (GBoxedCopyFunc) g_strdup, (GBoxedCopyFunc) tp_g_value_slice_dup);
1654 /* Don't change the accuracy as we used an address to get this position */
1655 g_hash_table_unref (priv->location);
1656 priv->location = new_location;
1657 g_object_notify ((GObject *) contact, "location");
1660 tp_clear_pointer (&resolved, g_hash_table_unref);
1661 g_object_unref (contact);
1665 update_geocode (EmpathyContact *contact)
1667 GeocodeForward *geocode;
1668 GHashTable *location;
1670 location = empathy_contact_get_location (contact);
1671 if (location == NULL ||
1672 g_hash_table_size (location) == 0)
1675 /* No need to search for position if contact published it */
1676 if (g_hash_table_lookup (location, EMPATHY_LOCATION_LAT) != NULL ||
1677 g_hash_table_lookup (location, EMPATHY_LOCATION_LON) != NULL)
1680 geocode = geocode_forward_new_for_params (location);
1681 if (geocode == NULL)
1684 geocode_forward_search_async (geocode, NULL, geocode_cb,
1685 g_object_ref (contact));
1687 g_object_unref (geocode);
1691 static EmpathyCapabilities
1692 tp_caps_to_capabilities (TpCapabilities *caps)
1694 EmpathyCapabilities capabilities = 0;
1696 if (tp_capabilities_supports_file_transfer (caps))
1697 capabilities |= EMPATHY_CAPABILITIES_FT;
1699 if (tp_capabilities_supports_stream_tubes (caps, TP_HANDLE_TYPE_CONTACT,
1701 capabilities |= EMPATHY_CAPABILITIES_RFB_STREAM_TUBE;
1703 if (tp_capabilities_supports_audio_video_call (caps, TP_HANDLE_TYPE_CONTACT))
1705 capabilities |= EMPATHY_CAPABILITIES_AUDIO;
1706 capabilities |= EMPATHY_CAPABILITIES_VIDEO;
1708 else if (tp_capabilities_supports_audio_call (caps, TP_HANDLE_TYPE_CONTACT))
1710 capabilities |= EMPATHY_CAPABILITIES_AUDIO;
1713 if (tp_capabilities_supports_sms (caps))
1714 capabilities |= EMPATHY_CAPABILITIES_SMS;
1716 return capabilities;
1720 set_capabilities_from_tp_caps (EmpathyContact *self,
1721 TpCapabilities *caps)
1723 EmpathyCapabilities capabilities;
1728 capabilities = tp_caps_to_capabilities (caps);
1729 empathy_contact_set_capabilities (self, capabilities);
1733 contact_set_avatar_from_tp_contact (EmpathyContact *contact)
1735 EmpathyContactPriv *priv = GET_PRIV (contact);
1739 mime = tp_contact_get_avatar_mime_type (priv->tp_contact);
1740 file = tp_contact_get_avatar_file (priv->tp_contact);
1744 EmpathyAvatar *avatar;
1748 GError *error = NULL;
1750 if (!g_file_load_contents (file, NULL, &data, &len, NULL, &error))
1752 DEBUG ("Failed to load avatar: %s", error->message);
1754 g_error_free (error);
1755 contact_set_avatar (contact, NULL);
1759 path = g_file_get_path (file);
1761 avatar = empathy_avatar_new ((guchar *) data, len, mime, path);
1763 contact_set_avatar (contact, avatar);
1764 empathy_avatar_unref (avatar);
1770 contact_set_avatar (contact, NULL);
1775 empathy_contact_dup_from_tp_contact (TpContact *tp_contact)
1777 EmpathyContact *contact = NULL;
1779 g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
1781 if (contacts_table == NULL)
1782 contacts_table = g_hash_table_new (g_direct_hash, g_direct_equal);
1784 contact = g_hash_table_lookup (contacts_table, tp_contact);
1786 if (contact == NULL)
1788 contact = empathy_contact_new (tp_contact);
1790 /* The hash table does not keep any ref.
1791 * contact keeps a ref to tp_contact, and is removed from the table in
1792 * contact_dispose() */
1793 g_hash_table_insert (contacts_table, tp_contact, contact);
1797 g_object_ref (contact);
1804 presence_cmp_func (EmpathyContact *a,
1807 FolksPresenceDetails *presence_a, *presence_b;
1809 presence_a = FOLKS_PRESENCE_DETAILS (empathy_contact_get_persona (a));
1810 presence_b = FOLKS_PRESENCE_DETAILS (empathy_contact_get_persona (b));
1812 /* We negate the result because we're sorting in reverse order (i.e. such that
1813 * the Personas with the highest presence are at the beginning of the list. */
1814 return -folks_presence_details_typecmp (
1815 folks_presence_details_get_presence_type (presence_a),
1816 folks_presence_details_get_presence_type (presence_b));
1820 voip_cmp_func (EmpathyContact *a,
1823 gboolean has_audio_a, has_audio_b;
1824 gboolean has_video_a, has_video_b;
1826 has_audio_a = empathy_contact_can_voip_audio (a);
1827 has_audio_b = empathy_contact_can_voip_audio (b);
1828 has_video_a = empathy_contact_can_voip_video (a);
1829 has_video_b = empathy_contact_can_voip_video (b);
1831 /* First check video */
1832 if (has_video_a == has_video_b)
1834 /* Use audio to to break tie */
1835 if (has_audio_a == has_audio_b)
1837 else if (has_audio_a)
1842 else if (has_video_a)
1849 ft_cmp_func (EmpathyContact *a,
1852 gboolean can_send_files_a, can_send_files_b;
1854 can_send_files_a = empathy_contact_can_send_files (a);
1855 can_send_files_b = empathy_contact_can_send_files (b);
1857 if (can_send_files_a == can_send_files_b)
1859 else if (can_send_files_a)
1866 rfb_stream_tube_cmp_func (EmpathyContact *a,
1869 gboolean rfb_a, rfb_b;
1871 rfb_a = empathy_contact_can_use_rfb_stream_tube (a);
1872 rfb_b = empathy_contact_can_use_rfb_stream_tube (b);
1882 /* Sort by presence as with presence_cmp_func(), but if the two contacts have
1883 * the same presence, prefer the one which can do both audio *and* video calls,
1884 * over the one which can only do one of the two. */
1886 voip_sort_func (EmpathyContact *a, EmpathyContact *b)
1888 gint presence_sort = presence_cmp_func (a, b);
1890 if (presence_sort != 0)
1891 return presence_sort;
1893 return voip_cmp_func (a, b);
1896 /* Sort by presence as with presence_cmp_func() and then break ties using the
1897 * most "capable" individual. So users will be able to pick more actions on
1898 * the contact in the "Contact" menu of the chat window. */
1900 chat_sort_func (EmpathyContact *a,
1905 result = presence_cmp_func (a, b);
1909 /* Prefer individual supporting file transfer */
1910 result = ft_cmp_func (a, b);
1914 /* Check audio/video capabilities */
1915 result = voip_cmp_func (a, b);
1919 /* Check 'Share my destop' feature */
1920 return rfb_stream_tube_cmp_func (a, b);
1924 get_sort_func_for_action (EmpathyActionType action_type)
1926 switch (action_type)
1928 case EMPATHY_ACTION_AUDIO_CALL:
1929 case EMPATHY_ACTION_VIDEO_CALL:
1930 return (GCompareFunc) voip_sort_func;
1931 case EMPATHY_ACTION_CHAT:
1932 return (GCompareFunc) chat_sort_func;
1933 case EMPATHY_ACTION_VIEW_LOGS:
1934 case EMPATHY_ACTION_SEND_FILE:
1935 case EMPATHY_ACTION_SHARE_MY_DESKTOP:
1937 return (GCompareFunc) presence_cmp_func;
1942 * empathy_contact_dup_best_for_action:
1943 * @individual: a #FolksIndividual
1944 * @action_type: the type of action to be performed on the contact
1946 * Chooses a #FolksPersona from the given @individual which is best-suited for
1947 * the given @action_type. "Best-suited" is determined by choosing the persona
1948 * with the highest presence out of all the personas which can perform the given
1949 * @action_type (e.g. are capable of video calling).
1951 * Return value: an #EmpathyContact for the best persona, or %NULL;
1952 * unref with g_object_unref()
1955 empathy_contact_dup_best_for_action (FolksIndividual *individual,
1956 EmpathyActionType action_type)
1961 EmpathyContact *best_contact = NULL;
1963 /* Build a list of EmpathyContacts that we can sort */
1964 personas = folks_individual_get_personas (individual);
1967 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
1968 while (gee_iterator_next (iter))
1970 FolksPersona *persona = gee_iterator_get (iter);
1971 TpContact *tp_contact;
1972 EmpathyContact *contact = NULL;
1974 if (!empathy_folks_persona_is_interesting (persona))
1977 tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona));
1978 if (tp_contact == NULL)
1981 contact = empathy_contact_dup_from_tp_contact (tp_contact);
1982 empathy_contact_set_persona (contact, FOLKS_PERSONA (persona));
1984 /* Only choose the contact if they're actually capable of the specified
1986 if (empathy_contact_can_do_action (contact, action_type))
1987 contacts = g_list_prepend (contacts, g_object_ref (contact));
1990 g_clear_object (&contact);
1991 g_clear_object (&persona);
1993 g_clear_object (&iter);
1995 /* Sort the contacts by some heuristic based on the action type, then take
1996 * the top contact. */
1997 if (contacts != NULL)
1999 contacts = g_list_sort (contacts, get_sort_func_for_action (action_type));
2000 best_contact = g_object_ref (contacts->data);
2003 g_list_foreach (contacts, (GFunc) g_object_unref, NULL);
2004 g_list_free (contacts);
2006 return best_contact;
2009 #define declare_contact_cb(name) \
2011 contact_##name##_cb (GObject *source, \
2012 GAsyncResult *result, \
2013 gpointer user_data) \
2015 TpContact *contact = (TpContact *) source; \
2016 GError *error = NULL; \
2018 if (!tp_contact_##name##_finish (contact, result, &error)) \
2020 DEBUG ("Failed to ##name## on %s\n", \
2021 tp_contact_get_identifier (contact)); \
2022 g_error_free (error); \
2026 declare_contact_cb (request_subscription)
2027 declare_contact_cb (authorize_publication)
2028 declare_contact_cb (unblock)
2031 empathy_contact_add_to_contact_list (EmpathyContact *self,
2032 const gchar *message)
2034 EmpathyContactPriv *priv = GET_PRIV (self);
2036 g_return_if_fail (priv->tp_contact != NULL);
2038 tp_contact_request_subscription_async (priv->tp_contact, message,
2039 contact_request_subscription_cb, NULL);
2041 tp_contact_authorize_publication_async (priv->tp_contact,
2042 contact_authorize_publication_cb, NULL);
2044 tp_contact_unblock_async (priv->tp_contact, contact_unblock_cb, NULL);
2047 declare_contact_cb (remove)
2050 empathy_contact_remove_from_contact_list (EmpathyContact *self)
2052 EmpathyContactPriv *priv = GET_PRIV (self);
2054 g_return_if_fail (priv->tp_contact != NULL);
2056 tp_contact_remove_async (priv->tp_contact, contact_remove_cb, NULL);