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>
26 #include <glib/gi18n-lib.h>
28 #include <telepathy-glib/account-manager.h>
29 #include <telepathy-glib/interfaces.h>
30 #include <telepathy-glib/util.h>
32 #include <folks/folks.h>
33 #include <folks/folks-telepathy.h>
36 #include <geocode-glib/geocode-glib.h>
39 #include "empathy-contact.h"
40 #include "empathy-camera-monitor.h"
41 #include "empathy-individual-manager.h"
42 #include "empathy-utils.h"
43 #include "empathy-enum-types.h"
44 #include "empathy-location.h"
46 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
47 #include "empathy-debug.h"
49 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContact)
51 TpContact *tp_contact;
53 FolksPersona *persona;
57 EmpathyAvatar *avatar;
58 TpConnectionPresenceType presence;
60 EmpathyCapabilities capabilities;
62 /* Location is composed of string keys and GValues.
63 * Example: a "city" key would have "Helsinki" as string GValue,
64 * a "latitude" would have 65.0 as double GValue.
66 * This is a super set of the location stored in TpContact as we can try add
67 * more fields by searching the address using geoclue.
74 static void contact_finalize (GObject *object);
75 static void contact_get_property (GObject *object, guint param_id,
76 GValue *value, GParamSpec *pspec);
77 static void contact_set_property (GObject *object, guint param_id,
78 const GValue *value, GParamSpec *pspec);
81 static void update_geocode (EmpathyContact *contact);
84 static void empathy_contact_set_location (EmpathyContact *contact,
85 GHashTable *location);
87 static void contact_set_client_types (EmpathyContact *contact,
88 const gchar * const *types);
90 static void set_capabilities_from_tp_caps (EmpathyContact *self,
91 TpCapabilities *caps);
93 static void contact_set_avatar (EmpathyContact *contact,
94 EmpathyAvatar *avatar);
95 static void contact_set_avatar_from_tp_contact (EmpathyContact *contact);
96 static gboolean contact_load_avatar_cache (EmpathyContact *contact,
99 G_DEFINE_TYPE (EmpathyContact, empathy_contact, G_TYPE_OBJECT);
112 PROP_PRESENCE_MESSAGE,
125 static guint signals[LAST_SIGNAL];
127 /* TpContact* -> EmpathyContact*, both borrowed ref */
128 static GHashTable *contacts_table = NULL;
131 tp_contact_notify_cb (TpContact *tp_contact,
135 EmpathyContactPriv *priv = GET_PRIV (contact);
137 /* Forward property notifications */
138 if (!tp_strdiff (param->name, "alias"))
139 g_object_notify (contact, "alias");
140 else if (!tp_strdiff (param->name, "presence-type")) {
141 TpConnectionPresenceType presence;
143 presence = empathy_contact_get_presence (EMPATHY_CONTACT (contact));
144 g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence,
146 priv->presence = presence;
147 g_object_notify (contact, "presence");
149 else if (!tp_strdiff (param->name, "identifier"))
150 g_object_notify (contact, "id");
151 else if (!tp_strdiff (param->name, "handle"))
152 g_object_notify (contact, "handle");
153 else if (!tp_strdiff (param->name, "location"))
155 GHashTable *location;
157 location = tp_contact_get_location (tp_contact);
158 /* This will start a geoclue search to find the address if needed */
159 empathy_contact_set_location (EMPATHY_CONTACT (contact), location);
161 else if (!tp_strdiff (param->name, "capabilities"))
163 set_capabilities_from_tp_caps (EMPATHY_CONTACT (contact),
164 tp_contact_get_capabilities (tp_contact));
166 else if (!tp_strdiff (param->name, "avatar-file"))
168 contact_set_avatar_from_tp_contact (EMPATHY_CONTACT (contact));
170 else if (!tp_strdiff (param->name, "client-types"))
172 contact_set_client_types (EMPATHY_CONTACT (contact),
173 tp_contact_get_client_types (tp_contact));
178 folks_persona_notify_cb (FolksPersona *folks_persona,
182 if (!tp_strdiff (param->name, "presence-message"))
183 g_object_notify (contact, "presence-message");
187 contact_dispose (GObject *object)
189 EmpathyContactPriv *priv = GET_PRIV (object);
191 if (priv->tp_contact != NULL)
193 g_signal_handlers_disconnect_by_func (priv->tp_contact,
194 tp_contact_notify_cb, object);
196 tp_clear_object (&priv->tp_contact);
199 g_object_unref (priv->account);
200 priv->account = NULL;
204 g_signal_handlers_disconnect_by_func (priv->persona,
205 folks_persona_notify_cb, object);
206 g_object_unref (priv->persona);
208 priv->persona = NULL;
210 if (priv->avatar != NULL)
212 empathy_avatar_unref (priv->avatar);
216 if (priv->location != NULL)
218 g_hash_table_unref (priv->location);
219 priv->location = NULL;
222 G_OBJECT_CLASS (empathy_contact_parent_class)->dispose (object);
226 contact_constructed (GObject *object)
228 EmpathyContact *contact = (EmpathyContact *) object;
229 EmpathyContactPriv *priv = GET_PRIV (contact);
230 GHashTable *location;
231 TpContact *self_contact;
232 const gchar * const *client_types;
234 if (priv->tp_contact == NULL)
237 priv->presence = empathy_contact_get_presence (contact);
239 location = tp_contact_get_location (priv->tp_contact);
240 if (location != NULL)
241 empathy_contact_set_location (contact, location);
243 client_types = tp_contact_get_client_types (priv->tp_contact);
244 if (client_types != NULL)
245 contact_set_client_types (contact, client_types);
247 set_capabilities_from_tp_caps (contact,
248 tp_contact_get_capabilities (priv->tp_contact));
250 contact_set_avatar_from_tp_contact (contact);
252 /* Set is-user property. Note that it could still be the handle is
253 * different from the connection's self handle, in the case the handle
254 * comes from a group interface. */
255 self_contact = tp_connection_get_self_contact (
256 tp_contact_get_connection (priv->tp_contact));
257 empathy_contact_set_is_user (contact, self_contact == priv->tp_contact);
259 g_signal_connect (priv->tp_contact, "notify",
260 G_CALLBACK (tp_contact_notify_cb), contact);
264 empathy_contact_class_init (EmpathyContactClass *class)
266 GObjectClass *object_class;
268 object_class = G_OBJECT_CLASS (class);
270 object_class->finalize = contact_finalize;
271 object_class->dispose = contact_dispose;
272 object_class->get_property = contact_get_property;
273 object_class->set_property = contact_set_property;
274 object_class->constructed = contact_constructed;
276 g_object_class_install_property (object_class,
278 g_param_spec_object ("tp-contact",
280 "The TpContact associated with the contact",
282 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
284 g_object_class_install_property (object_class,
286 g_param_spec_object ("account",
288 "The account associated with the contact",
290 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
292 g_object_class_install_property (object_class,
294 g_param_spec_object ("persona",
296 "The FolksPersona associated with the contact",
298 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
300 g_object_class_install_property (object_class,
302 g_param_spec_string ("id",
304 "String identifying contact",
306 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
308 g_object_class_install_property (object_class,
310 g_param_spec_string ("alias",
312 "An alias for the contact",
314 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
316 g_object_class_install_property (object_class,
318 g_param_spec_string ("logged-alias",
320 "The alias the user had when a message was logged, "
321 "only set when using empathy_contact_from_tpl_contact()",
323 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
325 g_object_class_install_property (object_class,
327 g_param_spec_boxed ("avatar",
331 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
333 g_object_class_install_property (object_class,
335 g_param_spec_uint ("presence",
337 "Presence of contact",
338 TP_CONNECTION_PRESENCE_TYPE_UNSET,
339 NUM_TP_CONNECTION_PRESENCE_TYPES,
340 TP_CONNECTION_PRESENCE_TYPE_UNSET,
341 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
343 g_object_class_install_property (object_class,
344 PROP_PRESENCE_MESSAGE,
345 g_param_spec_string ("presence-message",
346 "Contact presence message",
347 "Presence message of contact",
349 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
351 g_object_class_install_property (object_class,
353 g_param_spec_uint ("handle",
355 "The handle of the contact",
359 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
361 g_object_class_install_property (object_class,
363 g_param_spec_flags ("capabilities",
364 "Contact Capabilities",
365 "Capabilities of the contact",
366 EMPATHY_TYPE_CAPABILITIES,
367 EMPATHY_CAPABILITIES_UNKNOWN,
368 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
370 g_object_class_install_property (object_class,
372 g_param_spec_boolean ("is-user",
374 "Is contact the user",
376 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
379 g_object_class_install_property (object_class,
381 g_param_spec_boxed ("location",
383 "Physical location of the contact",
385 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
387 g_object_class_install_property (object_class,
389 g_param_spec_boxed ("client-types",
390 "Contact client types",
391 "Client types of the contact",
393 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
395 signals[PRESENCE_CHANGED] =
396 g_signal_new ("presence-changed",
397 G_TYPE_FROM_CLASS (class),
401 g_cclosure_marshal_generic,
406 g_type_class_add_private (object_class, sizeof (EmpathyContactPriv));
410 empathy_contact_init (EmpathyContact *contact)
412 EmpathyContactPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (contact,
413 EMPATHY_TYPE_CONTACT, EmpathyContactPriv);
415 contact->priv = priv;
417 priv->location = NULL;
418 priv->client_types = NULL;
423 contact_finalize (GObject *object)
425 EmpathyContactPriv *priv;
427 priv = GET_PRIV (object);
429 DEBUG ("finalize: %p", object);
431 g_clear_object (&priv->groups);
432 g_free (priv->alias);
433 g_free (priv->logged_alias);
435 g_strfreev (priv->client_types);
437 G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object);
441 empathy_contact_set_capabilities (EmpathyContact *contact,
442 EmpathyCapabilities capabilities)
444 EmpathyContactPriv *priv;
446 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
448 priv = GET_PRIV (contact);
450 if (priv->capabilities == capabilities)
453 priv->capabilities = capabilities;
455 g_object_notify (G_OBJECT (contact), "capabilities");
459 empathy_contact_set_id (EmpathyContact *contact,
462 EmpathyContactPriv *priv;
464 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
465 g_return_if_fail (id != NULL);
467 priv = GET_PRIV (contact);
469 /* We temporally ref the contact because it could be destroyed
470 * during the signal emition */
471 g_object_ref (contact);
472 if (tp_strdiff (id, priv->id))
475 priv->id = g_strdup (id);
477 g_object_notify (G_OBJECT (contact), "id");
478 if (EMP_STR_EMPTY (priv->alias))
479 g_object_notify (G_OBJECT (contact), "alias");
482 g_object_unref (contact);
486 empathy_contact_set_presence (EmpathyContact *contact,
487 TpConnectionPresenceType presence)
489 EmpathyContactPriv *priv;
490 TpConnectionPresenceType old_presence;
492 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
494 priv = GET_PRIV (contact);
496 if (presence == priv->presence)
499 old_presence = priv->presence;
500 priv->presence = presence;
502 g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence, old_presence);
504 g_object_notify (G_OBJECT (contact), "presence");
508 empathy_contact_set_presence_message (EmpathyContact *contact,
509 const gchar *message)
511 EmpathyContactPriv *priv = GET_PRIV (contact);
513 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
515 if (priv->persona != NULL)
517 folks_presence_details_set_presence_message (
518 FOLKS_PRESENCE_DETAILS (priv->persona), message);
523 empathy_contact_set_handle (EmpathyContact *contact,
526 EmpathyContactPriv *priv;
528 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
530 priv = GET_PRIV (contact);
532 g_object_ref (contact);
533 if (handle != priv->handle)
535 priv->handle = handle;
536 g_object_notify (G_OBJECT (contact), "handle");
538 g_object_unref (contact);
542 contact_get_property (GObject *object,
547 EmpathyContact *contact = EMPATHY_CONTACT (object);
551 case PROP_TP_CONTACT:
552 g_value_set_object (value, empathy_contact_get_tp_contact (contact));
555 g_value_set_object (value, empathy_contact_get_account (contact));
558 g_value_set_object (value, empathy_contact_get_persona (contact));
561 g_value_set_string (value, empathy_contact_get_id (contact));
564 g_value_set_string (value, empathy_contact_get_alias (contact));
566 case PROP_LOGGED_ALIAS:
567 g_value_set_string (value, empathy_contact_get_logged_alias (contact));
570 g_value_set_boxed (value, empathy_contact_get_avatar (contact));
573 g_value_set_uint (value, empathy_contact_get_presence (contact));
575 case PROP_PRESENCE_MESSAGE:
576 g_value_set_string (value, empathy_contact_get_presence_message (contact));
579 g_value_set_uint (value, empathy_contact_get_handle (contact));
581 case PROP_CAPABILITIES:
582 g_value_set_flags (value, empathy_contact_get_capabilities (contact));
585 g_value_set_boolean (value, empathy_contact_is_user (contact));
588 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
594 contact_set_property (GObject *object,
599 EmpathyContact *contact = EMPATHY_CONTACT (object);
600 EmpathyContactPriv *priv = GET_PRIV (object);
604 case PROP_TP_CONTACT:
605 priv->tp_contact = g_value_dup_object (value);
608 g_assert (priv->account == NULL);
609 priv->account = g_value_dup_object (value);
612 empathy_contact_set_persona (contact, g_value_get_object (value));
615 empathy_contact_set_id (contact, g_value_get_string (value));
618 empathy_contact_set_alias (contact, g_value_get_string (value));
620 case PROP_LOGGED_ALIAS:
621 g_assert (priv->logged_alias == NULL);
622 priv->logged_alias = g_value_dup_string (value);
625 empathy_contact_set_presence (contact, g_value_get_uint (value));
627 case PROP_PRESENCE_MESSAGE:
628 empathy_contact_set_presence_message (contact, g_value_get_string (value));
631 empathy_contact_set_handle (contact, g_value_get_uint (value));
633 case PROP_CAPABILITIES:
634 empathy_contact_set_capabilities (contact, g_value_get_flags (value));
637 empathy_contact_set_is_user (contact, g_value_get_boolean (value));
640 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
646 remove_tp_contact (gpointer data,
649 g_hash_table_remove (contacts_table, data);
652 static EmpathyContact *
653 empathy_contact_new (TpContact *tp_contact)
655 EmpathyContact *retval;
657 g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
659 retval = g_object_new (EMPATHY_TYPE_CONTACT,
660 "tp-contact", tp_contact,
663 g_object_weak_ref (G_OBJECT (retval), remove_tp_contact, tp_contact);
675 contact_is_tpl_entity (gpointer key,
679 EmpathyContact *contact = value;
680 FindContactData *data = user_data;
681 TpAccount *account = empathy_contact_get_account (contact);
682 const gchar *path = NULL;
685 path = tp_proxy_get_object_path (account);
687 return !tp_strdiff (empathy_contact_get_id (contact),
688 tpl_entity_get_identifier (data->entity)) &&
689 !tp_strdiff (tp_proxy_get_object_path (data->account), path);
693 get_contacts_cb (GObject *source,
694 GAsyncResult *result,
697 TpWeakRef *wr = user_data;
698 EmpathyContactPriv *priv;
699 EmpathyContact *self;
701 self = tp_weak_ref_dup_object (wr);
705 priv = GET_PRIV (self);
707 g_return_if_fail (priv->tp_contact == NULL);
709 priv->tp_contact = tp_connection_dup_contact_by_id_finish (
710 TP_CONNECTION (source), result, NULL);
711 if (priv->tp_contact == NULL)
714 g_object_notify (G_OBJECT (self), "tp-contact");
716 /* Update capabilities now that we have a TpContact */
717 set_capabilities_from_tp_caps (self,
718 tp_contact_get_capabilities (priv->tp_contact));
721 g_clear_object (&self);
722 tp_weak_ref_destroy (wr);
726 empathy_contact_from_tpl_contact (TpAccount *account,
727 TplEntity *tpl_entity)
729 EmpathyContact *retval;
731 EmpathyContact *existing_contact = NULL;
733 g_return_val_if_fail (TPL_IS_ENTITY (tpl_entity), NULL);
735 if (contacts_table != NULL)
737 FindContactData data;
739 data.entity = tpl_entity;
740 data.account = account;
742 existing_contact = g_hash_table_find (contacts_table,
743 contact_is_tpl_entity, &data);
746 if (existing_contact != NULL)
748 retval = g_object_new (EMPATHY_TYPE_CONTACT,
749 "tp-contact", empathy_contact_get_tp_contact (existing_contact),
750 "logged-alias", tpl_entity_get_alias (tpl_entity),
758 is_user = (TPL_ENTITY_SELF == tpl_entity_get_entity_type (tpl_entity));
760 id = tpl_entity_get_identifier (tpl_entity);
762 retval = g_object_new (EMPATHY_TYPE_CONTACT,
764 "alias", tpl_entity_get_alias (tpl_entity),
769 /* Try to get a TpContact associated to have at least contact
770 * capabilities if possible. This is useful for CM supporting calling
771 * offline contacts for example. */
772 conn = tp_account_get_connection (account);
775 TpContactFeature features[] = { TP_CONTACT_FEATURE_CAPABILITIES };
776 conn = tp_account_get_connection (account);
778 tp_connection_dup_contact_by_id_async (conn, id,
779 G_N_ELEMENTS (features), features, get_contacts_cb,
780 tp_weak_ref_new (retval, NULL, NULL));
784 if (!EMP_STR_EMPTY (tpl_entity_get_avatar_token (tpl_entity)))
785 contact_load_avatar_cache (retval,
786 tpl_entity_get_avatar_token (tpl_entity));
792 empathy_contact_get_tp_contact (EmpathyContact *contact)
794 EmpathyContactPriv *priv;
796 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
798 priv = GET_PRIV (contact);
800 return priv->tp_contact;
804 empathy_contact_get_id (EmpathyContact *contact)
806 EmpathyContactPriv *priv;
808 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
810 priv = GET_PRIV (contact);
812 if (priv->tp_contact != NULL)
813 return tp_contact_get_identifier (priv->tp_contact);
819 empathy_contact_get_alias (EmpathyContact *contact)
821 EmpathyContactPriv *priv;
822 const gchar *alias = NULL;
824 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
826 priv = GET_PRIV (contact);
828 if (!EMP_STR_EMPTY (priv->alias))
830 else if (priv->tp_contact != NULL)
831 alias = tp_contact_get_alias (priv->tp_contact);
833 if (!EMP_STR_EMPTY (alias))
836 return empathy_contact_get_id (contact);
840 empathy_contact_get_logged_alias (EmpathyContact *contact)
842 EmpathyContactPriv *priv;
844 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
846 priv = GET_PRIV (contact);
848 if (priv->logged_alias != NULL)
849 return priv->logged_alias;
851 return empathy_contact_get_alias (contact);
855 empathy_contact_set_alias (EmpathyContact *contact,
858 EmpathyContactPriv *priv;
859 FolksPersona *persona;
861 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
863 priv = GET_PRIV (contact);
865 g_object_ref (contact);
867 /* Set the alias on the persona if possible */
868 persona = empathy_contact_get_persona (contact);
869 if (persona != NULL && FOLKS_IS_ALIAS_DETAILS (persona))
871 DEBUG ("Setting alias for contact %s to %s",
872 empathy_contact_get_id (contact), alias);
874 folks_alias_details_set_alias (FOLKS_ALIAS_DETAILS (persona), alias);
877 if (tp_strdiff (alias, priv->alias))
879 g_free (priv->alias);
880 priv->alias = g_strdup (alias);
881 g_object_notify (G_OBJECT (contact), "alias");
884 g_object_unref (contact);
888 groups_change_group_cb (GObject *source,
889 GAsyncResult *result,
892 FolksGroupDetails *group_details = FOLKS_GROUP_DETAILS (source);
893 GError *error = NULL;
895 folks_group_details_change_group_finish (group_details, result, &error);
898 g_warning ("failed to change group: %s", error->message);
899 g_clear_error (&error);
904 empathy_contact_change_group (EmpathyContact *contact, const gchar *group,
907 EmpathyContactPriv *priv;
908 FolksPersona *persona;
910 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
911 g_return_if_fail (group != NULL);
913 priv = GET_PRIV (contact);
915 /* Normally pass through the changes to the persona */
916 persona = empathy_contact_get_persona (contact);
919 if (FOLKS_IS_GROUP_DETAILS (persona))
920 folks_group_details_change_group (FOLKS_GROUP_DETAILS (persona), group,
921 is_member, groups_change_group_cb, contact);
925 /* If the persona doesn't exist yet, we have to cache the changes until it
927 if (priv->groups == NULL)
929 priv->groups = gee_hash_set_new (G_TYPE_STRING, (GBoxedCopyFunc) g_strdup,
930 g_free, g_str_hash, g_str_equal);
933 gee_collection_add (GEE_COLLECTION (priv->groups), group);
937 empathy_contact_get_avatar (EmpathyContact *contact)
939 EmpathyContactPriv *priv;
941 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
943 priv = GET_PRIV (contact);
949 contact_set_avatar (EmpathyContact *contact,
950 EmpathyAvatar *avatar)
952 EmpathyContactPriv *priv;
954 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
956 priv = GET_PRIV (contact);
958 if (priv->avatar == avatar)
963 empathy_avatar_unref (priv->avatar);
968 priv->avatar = empathy_avatar_ref (avatar);
970 g_object_notify (G_OBJECT (contact), "avatar");
974 empathy_contact_get_account (EmpathyContact *contact)
976 EmpathyContactPriv *priv;
978 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
980 priv = GET_PRIV (contact);
982 if (priv->account == NULL && priv->tp_contact != NULL)
984 TpConnection *connection;
986 /* FIXME: This assume the account manager already exists */
987 connection = tp_contact_get_connection (priv->tp_contact);
989 g_object_ref (tp_connection_get_account (connection));
992 return priv->account;
996 empathy_contact_get_persona (EmpathyContact *contact)
998 EmpathyContactPriv *priv;
1000 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
1002 priv = GET_PRIV (contact);
1004 if (priv->persona == NULL && priv->tp_contact != NULL)
1006 TpfPersona *persona;
1008 persona = tpf_persona_dup_for_contact (priv->tp_contact);
1009 if (persona != NULL)
1011 empathy_contact_set_persona (contact, (FolksPersona *) persona);
1012 g_object_unref (persona);
1016 return priv->persona;
1020 empathy_contact_set_persona (EmpathyContact *contact,
1021 FolksPersona *persona)
1023 EmpathyContactPriv *priv;
1025 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1026 g_return_if_fail (TPF_IS_PERSONA (persona));
1028 priv = GET_PRIV (contact);
1030 if (persona == priv->persona)
1033 if (priv->persona != NULL)
1035 g_signal_handlers_disconnect_by_func (priv->persona,
1036 folks_persona_notify_cb, contact);
1037 g_object_unref (priv->persona);
1039 priv->persona = g_object_ref (persona);
1041 g_signal_connect (priv->persona, "notify",
1042 G_CALLBACK (folks_persona_notify_cb), contact);
1044 g_object_notify (G_OBJECT (contact), "persona");
1046 /* Set the persona's alias, since ours could've been set using
1047 * empathy_contact_set_alias() before we had a persona; this happens when
1048 * adding a contact. */
1049 if (priv->alias != NULL)
1050 empathy_contact_set_alias (contact, priv->alias);
1052 /* Set the persona's groups */
1053 if (priv->groups != NULL)
1055 folks_group_details_set_groups (FOLKS_GROUP_DETAILS (persona),
1056 GEE_SET (priv->groups));
1057 g_object_unref (priv->groups);
1058 priv->groups = NULL;
1063 empathy_contact_get_connection (EmpathyContact *contact)
1065 EmpathyContactPriv *priv;
1067 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
1069 priv = GET_PRIV (contact);
1071 if (priv->tp_contact != NULL)
1072 return tp_contact_get_connection (priv->tp_contact);
1077 TpConnectionPresenceType
1078 empathy_contact_get_presence (EmpathyContact *contact)
1080 EmpathyContactPriv *priv;
1082 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact),
1083 TP_CONNECTION_PRESENCE_TYPE_UNSET);
1085 priv = GET_PRIV (contact);
1087 if (priv->tp_contact != NULL)
1088 return tp_contact_get_presence_type (priv->tp_contact);
1090 return priv->presence;
1094 empathy_contact_get_presence_message (EmpathyContact *contact)
1096 EmpathyContactPriv *priv;
1098 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
1100 priv = GET_PRIV (contact);
1102 if (priv->persona != NULL)
1103 return folks_presence_details_get_presence_message (
1104 FOLKS_PRESENCE_DETAILS (priv->persona));
1106 if (priv->tp_contact != NULL)
1107 return tp_contact_get_presence_message (priv->tp_contact);
1113 empathy_contact_get_handle (EmpathyContact *contact)
1115 EmpathyContactPriv *priv;
1117 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
1119 priv = GET_PRIV (contact);
1121 if (priv->tp_contact != NULL)
1122 return tp_contact_get_handle (priv->tp_contact);
1124 return priv->handle;
1128 empathy_contact_get_capabilities (EmpathyContact *contact)
1130 EmpathyContactPriv *priv;
1132 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
1134 priv = GET_PRIV (contact);
1136 return priv->capabilities;
1140 empathy_contact_is_user (EmpathyContact *contact)
1142 EmpathyContactPriv *priv;
1144 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1146 priv = GET_PRIV (contact);
1148 return priv->is_user;
1152 empathy_contact_set_is_user (EmpathyContact *contact,
1155 EmpathyContactPriv *priv;
1157 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1159 priv = GET_PRIV (contact);
1161 if (priv->is_user == is_user)
1164 priv->is_user = is_user;
1166 g_object_notify (G_OBJECT (contact), "is-user");
1170 empathy_contact_is_online (EmpathyContact *contact)
1172 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1174 switch (empathy_contact_get_presence (contact))
1176 case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
1177 case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
1178 case TP_CONNECTION_PRESENCE_TYPE_ERROR:
1180 /* Contacts without presence are considered online so we can display IRC
1181 * contacts in rooms. */
1182 case TP_CONNECTION_PRESENCE_TYPE_UNSET:
1183 case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
1184 case TP_CONNECTION_PRESENCE_TYPE_AWAY:
1185 case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
1186 case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
1187 case TP_CONNECTION_PRESENCE_TYPE_BUSY:
1194 empathy_contact_get_status (EmpathyContact *contact)
1196 const gchar *message;
1198 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), "");
1200 message = empathy_contact_get_presence_message (contact);
1201 if (!EMP_STR_EMPTY (message))
1204 return empathy_presence_get_default_message (
1205 empathy_contact_get_presence (contact));
1209 empathy_contact_can_sms (EmpathyContact *contact)
1211 EmpathyContactPriv *priv;
1213 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1215 priv = GET_PRIV (contact);
1217 return priv->capabilities & EMPATHY_CAPABILITIES_SMS;
1221 empathy_contact_can_voip (EmpathyContact *contact)
1223 EmpathyContactPriv *priv;
1225 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1227 priv = GET_PRIV (contact);
1229 return priv->capabilities & (EMPATHY_CAPABILITIES_AUDIO |
1230 EMPATHY_CAPABILITIES_VIDEO);
1234 empathy_contact_can_voip_audio (EmpathyContact *contact)
1236 EmpathyContactPriv *priv;
1238 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1240 priv = GET_PRIV (contact);
1242 return priv->capabilities & EMPATHY_CAPABILITIES_AUDIO;
1246 empathy_contact_can_voip_video (EmpathyContact *contact)
1248 EmpathyContactPriv *priv;
1250 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1252 priv = GET_PRIV (contact);
1254 return priv->capabilities & EMPATHY_CAPABILITIES_VIDEO;
1258 empathy_contact_can_send_files (EmpathyContact *contact)
1260 EmpathyContactPriv *priv;
1262 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1264 priv = GET_PRIV (contact);
1266 return priv->capabilities & EMPATHY_CAPABILITIES_FT;
1270 empathy_contact_can_use_rfb_stream_tube (EmpathyContact *contact)
1272 EmpathyContactPriv *priv;
1274 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1276 priv = GET_PRIV (contact);
1278 return priv->capabilities & EMPATHY_CAPABILITIES_RFB_STREAM_TUBE;
1282 contact_has_log (EmpathyContact *contact)
1284 TplLogManager *manager;
1288 manager = tpl_log_manager_dup_singleton ();
1289 entity = tpl_entity_new (empathy_contact_get_id (contact),
1290 TPL_ENTITY_CONTACT, NULL, NULL);
1292 have_log = tpl_log_manager_exists (manager,
1293 empathy_contact_get_account (contact), entity, TPL_EVENT_MASK_TEXT);
1295 g_object_unref (entity);
1296 g_object_unref (manager);
1302 empathy_contact_can_do_action (EmpathyContact *self,
1303 EmpathyActionType action_type)
1305 gboolean sensitivity = FALSE;
1307 switch (action_type)
1309 case EMPATHY_ACTION_CHAT:
1312 case EMPATHY_ACTION_SMS:
1313 sensitivity = empathy_contact_can_sms (self);
1315 case EMPATHY_ACTION_AUDIO_CALL:
1316 sensitivity = empathy_contact_can_voip_audio (self);
1318 case EMPATHY_ACTION_VIDEO_CALL:
1319 sensitivity = empathy_contact_can_voip_video (self);
1321 case EMPATHY_ACTION_VIEW_LOGS:
1322 sensitivity = contact_has_log (self);
1324 case EMPATHY_ACTION_SEND_FILE:
1325 sensitivity = empathy_contact_can_send_files (self);
1327 case EMPATHY_ACTION_SHARE_MY_DESKTOP:
1328 sensitivity = empathy_contact_can_use_rfb_stream_tube (self);
1331 g_assert_not_reached ();
1334 return (sensitivity ? TRUE : FALSE);
1338 contact_get_avatar_filename (EmpathyContact *contact,
1344 gchar *token_escaped;
1346 if (EMP_STR_EMPTY (empathy_contact_get_id (contact)))
1349 token_escaped = tp_escape_as_identifier (token);
1350 account = empathy_contact_get_account (contact);
1352 avatar_path = g_build_filename (g_get_user_cache_dir (),
1355 tp_account_get_cm_name (account),
1356 tp_account_get_protocol_name (account),
1358 g_mkdir_with_parents (avatar_path, 0700);
1360 avatar_file = g_build_filename (avatar_path, token_escaped, NULL);
1362 g_free (token_escaped);
1363 g_free (avatar_path);
1369 contact_load_avatar_cache (EmpathyContact *contact,
1372 EmpathyAvatar *avatar = NULL;
1376 GError *error = NULL;
1378 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1379 g_return_val_if_fail (!EMP_STR_EMPTY (token), FALSE);
1381 /* Load the avatar from file if it exists */
1382 filename = contact_get_avatar_filename (contact, token);
1383 if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
1385 if (!g_file_get_contents (filename, &data, &len, &error))
1387 DEBUG ("Failed to load avatar from cache: %s",
1388 error ? error->message : "No error given");
1389 g_clear_error (&error);
1395 DEBUG ("Avatar loaded from %s", filename);
1396 avatar = empathy_avatar_new ((guchar *) data, len, NULL, filename);
1397 contact_set_avatar (contact, avatar);
1398 empathy_avatar_unref (avatar);
1404 return data != NULL;
1408 empathy_avatar_get_type (void)
1410 static GType type_id = 0;
1414 type_id = g_boxed_type_register_static ("EmpathyAvatar",
1415 (GBoxedCopyFunc) empathy_avatar_ref,
1416 (GBoxedFreeFunc) empathy_avatar_unref);
1423 * empathy_avatar_new:
1424 * @data: the avatar data
1425 * @len: the size of avatar data
1426 * @format: the mime type of the avatar image
1427 * @filename: the filename where the avatar is stored in cache
1429 * Create a #EmpathyAvatar from the provided data.
1431 * Returns: a new #EmpathyAvatar
1434 empathy_avatar_new (const guchar *data,
1436 const gchar *format,
1437 const gchar *filename)
1439 EmpathyAvatar *avatar;
1441 avatar = g_slice_new0 (EmpathyAvatar);
1442 avatar->data = g_memdup (data, len);
1444 avatar->format = g_strdup (format);
1445 avatar->filename = g_strdup (filename);
1446 avatar->refcount = 1;
1452 empathy_avatar_unref (EmpathyAvatar *avatar)
1454 g_return_if_fail (avatar != NULL);
1457 if (avatar->refcount == 0)
1459 g_free (avatar->data);
1460 g_free (avatar->format);
1461 g_free (avatar->filename);
1462 g_slice_free (EmpathyAvatar, avatar);
1467 empathy_avatar_ref (EmpathyAvatar *avatar)
1469 g_return_val_if_fail (avatar != NULL, NULL);
1477 * empathy_avatar_save_to_file:
1478 * @avatar: the avatar
1479 * @filename: name of a file to write avatar to
1480 * @error: return location for a GError, or NULL
1482 * Save the avatar to a file named filename
1484 * Returns: %TRUE on success, %FALSE if an error occurred
1487 empathy_avatar_save_to_file (EmpathyAvatar *self,
1488 const gchar *filename,
1491 return g_file_set_contents (filename, (const gchar *) self->data, self->len,
1496 * empathy_contact_get_location:
1497 * @contact: an #EmpathyContact
1499 * Returns the user's location if available. The keys are defined in
1500 * empathy-location.h. If the contact doesn't have location
1501 * information, the GHashTable will be empthy. Use #g_hash_table_unref when
1502 * you are done with the #GHashTable.
1504 * It is composed of string keys and GValues. Keys are
1505 * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
1506 * Example: a "city" key would have "Helsinki" as string GValue,
1507 * a "latitude" would have 65.0 as double GValue.
1509 * Returns: a #GHashTable of location values
1512 empathy_contact_get_location (EmpathyContact *contact)
1514 EmpathyContactPriv *priv;
1516 g_return_val_if_fail (EMPATHY_CONTACT (contact), NULL);
1518 priv = GET_PRIV (contact);
1520 return priv->location;
1524 * empathy_contact_set_location:
1525 * @contact: an #EmpathyContact
1526 * @location: a #GHashTable of the location
1528 * Sets the user's location based on the location #GHashTable passed.
1529 * It is composed of string keys and GValues. Keys are
1530 * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
1531 * Example: a "city" key would have "Helsinki" as string GValue,
1532 * a "latitude" would have 65.0 as double GValue.
1535 empathy_contact_set_location (EmpathyContact *contact,
1536 GHashTable *location)
1538 EmpathyContactPriv *priv;
1540 g_return_if_fail (EMPATHY_CONTACT (contact));
1541 g_return_if_fail (location != NULL);
1543 priv = GET_PRIV (contact);
1545 if (priv->location != NULL)
1546 g_hash_table_unref (priv->location);
1548 priv->location = g_hash_table_ref (location);
1550 update_geocode (contact);
1552 g_object_notify (G_OBJECT (contact), "location");
1555 const gchar * const *
1556 empathy_contact_get_client_types (EmpathyContact *contact)
1558 EmpathyContactPriv *priv;
1560 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
1562 priv = GET_PRIV (contact);
1564 return (const gchar * const *) priv->client_types;
1568 contact_set_client_types (EmpathyContact *contact,
1569 const gchar * const *client_types)
1571 EmpathyContactPriv *priv = GET_PRIV (contact);
1573 if (priv->client_types != NULL)
1574 g_strfreev (priv->client_types);
1576 priv->client_types = g_strdupv ((gchar **) client_types);
1577 g_object_notify (G_OBJECT (contact), "client-types");
1581 * empathy_contact_equal:
1582 * @contact1: an #EmpathyContact
1583 * @contact2: an #EmpathyContact
1585 * Returns FALSE if one of the contacts is NULL but the other is not.
1586 * Otherwise returns TRUE if both pointer are equal or if they bith
1587 * refer to the same id.
1588 * It's only necessary to call this function if your contact objects
1589 * come from logs where contacts are created dynamically and comparing
1590 * pointers is not enough.
1593 empathy_contact_equal (gconstpointer contact1,
1594 gconstpointer contact2)
1601 if ((contact1 == NULL) != (contact2 == NULL)) {
1604 if (contact1 == contact2) {
1607 c1 = EMPATHY_CONTACT (contact1);
1608 c2 = EMPATHY_CONTACT (contact2);
1609 id1 = empathy_contact_get_id (c1);
1610 id2 = empathy_contact_get_id (c2);
1611 if (!tp_strdiff (id1, id2)) {
1618 /* This callback is called by geocode-glib when it found a position
1619 * for the given address. A position is necessary for a contact
1620 * to show up on the map
1623 geocode_cb (GObject *source,
1624 GAsyncResult *result,
1627 EmpathyContact *contact = user_data;
1628 EmpathyContactPriv *priv = GET_PRIV (contact);
1629 GError *error = NULL;
1630 GHashTable *new_location;
1631 GHashTable *resolved = NULL;
1632 gdouble latitude, longitude;
1634 if (priv->location == NULL)
1637 resolved = geocode_object_resolve_finish (GEOCODE_OBJECT (source), result,
1640 if (resolved == NULL)
1642 DEBUG ("Failed to resolve geocode: %s", error->message);
1643 g_error_free (error);
1647 if (!geocode_object_get_coords (resolved, &longitude, &latitude))
1650 new_location = tp_asv_new (
1651 EMPATHY_LOCATION_LAT, G_TYPE_DOUBLE, latitude,
1652 EMPATHY_LOCATION_LON, G_TYPE_DOUBLE, longitude,
1655 DEBUG ("\t - Latitude: %f", latitude);
1656 DEBUG ("\t - Longitude: %f", longitude);
1658 /* Copy remaning fields. LAT and LON were not defined so we won't overwrite
1659 * the values we just set. */
1660 tp_g_hash_table_update (new_location, priv->location,
1661 (GBoxedCopyFunc) g_strdup, (GBoxedCopyFunc) tp_g_value_slice_dup);
1663 /* Don't change the accuracy as we used an address to get this position */
1664 g_hash_table_unref (priv->location);
1665 priv->location = new_location;
1666 g_object_notify ((GObject *) contact, "location");
1669 tp_clear_pointer (&resolved, g_hash_table_unref);
1670 g_object_unref (contact);
1674 update_geocode (EmpathyContact *contact)
1676 GeocodeObject *geocode;
1677 GHashTable *location;
1679 location = empathy_contact_get_location (contact);
1680 if (location == NULL ||
1681 g_hash_table_size (location) == 0)
1684 /* No need to search for position if contact published it */
1685 if (g_hash_table_lookup (location, EMPATHY_LOCATION_LAT) != NULL ||
1686 g_hash_table_lookup (location, EMPATHY_LOCATION_LON) != NULL)
1689 geocode = geocode_object_new_for_params (location);
1690 if (geocode == NULL)
1693 geocode_object_resolve_async (geocode, NULL, geocode_cb,
1694 g_object_ref (contact));
1696 g_object_unref (geocode);
1700 static EmpathyCapabilities
1701 tp_caps_to_capabilities (TpCapabilities *caps)
1703 EmpathyCapabilities capabilities = 0;
1705 if (tp_capabilities_supports_file_transfer (caps))
1706 capabilities |= EMPATHY_CAPABILITIES_FT;
1708 if (tp_capabilities_supports_stream_tubes (caps, TP_HANDLE_TYPE_CONTACT,
1710 capabilities |= EMPATHY_CAPABILITIES_RFB_STREAM_TUBE;
1712 if (tp_capabilities_supports_audio_video_call (caps, TP_HANDLE_TYPE_CONTACT))
1714 capabilities |= EMPATHY_CAPABILITIES_AUDIO;
1715 capabilities |= EMPATHY_CAPABILITIES_VIDEO;
1717 else if (tp_capabilities_supports_audio_call (caps, TP_HANDLE_TYPE_CONTACT))
1719 capabilities |= EMPATHY_CAPABILITIES_AUDIO;
1722 if (tp_capabilities_supports_sms (caps))
1723 capabilities |= EMPATHY_CAPABILITIES_SMS;
1725 return capabilities;
1729 set_capabilities_from_tp_caps (EmpathyContact *self,
1730 TpCapabilities *caps)
1732 EmpathyCapabilities capabilities;
1737 capabilities = tp_caps_to_capabilities (caps);
1738 empathy_contact_set_capabilities (self, capabilities);
1742 contact_set_avatar_from_tp_contact (EmpathyContact *contact)
1744 EmpathyContactPriv *priv = GET_PRIV (contact);
1748 mime = tp_contact_get_avatar_mime_type (priv->tp_contact);
1749 file = tp_contact_get_avatar_file (priv->tp_contact);
1753 EmpathyAvatar *avatar;
1757 GError *error = NULL;
1759 if (!g_file_load_contents (file, NULL, &data, &len, NULL, &error))
1761 DEBUG ("Failed to load avatar: %s", error->message);
1763 g_error_free (error);
1764 contact_set_avatar (contact, NULL);
1768 path = g_file_get_path (file);
1770 avatar = empathy_avatar_new ((guchar *) data, len, mime, path);
1772 contact_set_avatar (contact, avatar);
1773 empathy_avatar_unref (avatar);
1779 contact_set_avatar (contact, NULL);
1784 empathy_contact_dup_from_tp_contact (TpContact *tp_contact)
1786 EmpathyContact *contact = NULL;
1788 g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
1790 if (contacts_table == NULL)
1791 contacts_table = g_hash_table_new (g_direct_hash, g_direct_equal);
1793 contact = g_hash_table_lookup (contacts_table, tp_contact);
1795 if (contact == NULL)
1797 contact = empathy_contact_new (tp_contact);
1799 /* The hash table does not keep any ref.
1800 * contact keeps a ref to tp_contact, and is removed from the table in
1801 * contact_dispose() */
1802 g_hash_table_insert (contacts_table, tp_contact, contact);
1806 g_object_ref (contact);
1813 presence_cmp_func (EmpathyContact *a,
1816 FolksPresenceDetails *presence_a, *presence_b;
1818 presence_a = FOLKS_PRESENCE_DETAILS (empathy_contact_get_persona (a));
1819 presence_b = FOLKS_PRESENCE_DETAILS (empathy_contact_get_persona (b));
1821 /* We negate the result because we're sorting in reverse order (i.e. such that
1822 * the Personas with the highest presence are at the beginning of the list. */
1823 return -folks_presence_details_typecmp (
1824 folks_presence_details_get_presence_type (presence_a),
1825 folks_presence_details_get_presence_type (presence_b));
1829 voip_cmp_func (EmpathyContact *a,
1832 gboolean has_audio_a, has_audio_b;
1833 gboolean has_video_a, has_video_b;
1835 has_audio_a = empathy_contact_can_voip_audio (a);
1836 has_audio_b = empathy_contact_can_voip_audio (b);
1837 has_video_a = empathy_contact_can_voip_video (a);
1838 has_video_b = empathy_contact_can_voip_video (b);
1840 /* First check video */
1841 if (has_video_a == has_video_b)
1843 /* Use audio to to break tie */
1844 if (has_audio_a == has_audio_b)
1846 else if (has_audio_a)
1851 else if (has_video_a)
1858 ft_cmp_func (EmpathyContact *a,
1861 gboolean can_send_files_a, can_send_files_b;
1863 can_send_files_a = empathy_contact_can_send_files (a);
1864 can_send_files_b = empathy_contact_can_send_files (b);
1866 if (can_send_files_a == can_send_files_b)
1868 else if (can_send_files_a)
1875 rfb_stream_tube_cmp_func (EmpathyContact *a,
1878 gboolean rfb_a, rfb_b;
1880 rfb_a = empathy_contact_can_use_rfb_stream_tube (a);
1881 rfb_b = empathy_contact_can_use_rfb_stream_tube (b);
1891 /* Sort by presence as with presence_cmp_func(), but if the two contacts have
1892 * the same presence, prefer the one which can do both audio *and* video calls,
1893 * over the one which can only do one of the two. */
1895 voip_sort_func (EmpathyContact *a, EmpathyContact *b)
1897 gint presence_sort = presence_cmp_func (a, b);
1899 if (presence_sort != 0)
1900 return presence_sort;
1902 return voip_cmp_func (a, b);
1905 /* Sort by presence as with presence_cmp_func() and then break ties using the
1906 * most "capable" individual. So users will be able to pick more actions on
1907 * the contact in the "Contact" menu of the chat window. */
1909 chat_sort_func (EmpathyContact *a,
1914 result = presence_cmp_func (a, b);
1918 /* Prefer individual supporting file transfer */
1919 result = ft_cmp_func (a, b);
1923 /* Check audio/video capabilities */
1924 result = voip_cmp_func (a, b);
1928 /* Check 'Share my destop' feature */
1929 return rfb_stream_tube_cmp_func (a, b);
1933 get_sort_func_for_action (EmpathyActionType action_type)
1935 switch (action_type)
1937 case EMPATHY_ACTION_AUDIO_CALL:
1938 case EMPATHY_ACTION_VIDEO_CALL:
1939 return (GCompareFunc) voip_sort_func;
1940 case EMPATHY_ACTION_CHAT:
1941 return (GCompareFunc) chat_sort_func;
1942 case EMPATHY_ACTION_VIEW_LOGS:
1943 case EMPATHY_ACTION_SEND_FILE:
1944 case EMPATHY_ACTION_SHARE_MY_DESKTOP:
1946 return (GCompareFunc) presence_cmp_func;
1951 * empathy_contact_dup_best_for_action:
1952 * @individual: a #FolksIndividual
1953 * @action_type: the type of action to be performed on the contact
1955 * Chooses a #FolksPersona from the given @individual which is best-suited for
1956 * the given @action_type. "Best-suited" is determined by choosing the persona
1957 * with the highest presence out of all the personas which can perform the given
1958 * @action_type (e.g. are capable of video calling).
1960 * Return value: an #EmpathyContact for the best persona, or %NULL;
1961 * unref with g_object_unref()
1964 empathy_contact_dup_best_for_action (FolksIndividual *individual,
1965 EmpathyActionType action_type)
1970 EmpathyContact *best_contact = NULL;
1972 /* Build a list of EmpathyContacts that we can sort */
1973 personas = folks_individual_get_personas (individual);
1976 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
1977 while (gee_iterator_next (iter))
1979 FolksPersona *persona = gee_iterator_get (iter);
1980 TpContact *tp_contact;
1981 EmpathyContact *contact = NULL;
1983 if (!empathy_folks_persona_is_interesting (persona))
1986 tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona));
1987 if (tp_contact == NULL)
1990 contact = empathy_contact_dup_from_tp_contact (tp_contact);
1991 empathy_contact_set_persona (contact, FOLKS_PERSONA (persona));
1993 /* Only choose the contact if they're actually capable of the specified
1995 if (empathy_contact_can_do_action (contact, action_type))
1996 contacts = g_list_prepend (contacts, g_object_ref (contact));
1999 g_clear_object (&contact);
2000 g_clear_object (&persona);
2002 g_clear_object (&iter);
2004 /* Sort the contacts by some heuristic based on the action type, then take
2005 * the top contact. */
2006 if (contacts != NULL)
2008 contacts = g_list_sort (contacts, get_sort_func_for_action (action_type));
2009 best_contact = g_object_ref (contacts->data);
2012 g_list_foreach (contacts, (GFunc) g_object_unref, NULL);
2013 g_list_free (contacts);
2015 return best_contact;
2018 #define declare_contact_cb(name) \
2020 contact_##name##_cb (GObject *source, \
2021 GAsyncResult *result, \
2022 gpointer user_data) \
2024 TpContact *contact = (TpContact *) source; \
2025 GError *error = NULL; \
2027 if (!tp_contact_##name##_finish (contact, result, &error)) \
2029 DEBUG ("Failed to ##name## on %s\n", \
2030 tp_contact_get_identifier (contact)); \
2031 g_error_free (error); \
2035 declare_contact_cb (request_subscription)
2036 declare_contact_cb (authorize_publication)
2037 declare_contact_cb (unblock)
2040 empathy_contact_add_to_contact_list (EmpathyContact *self,
2041 const gchar *message)
2043 EmpathyContactPriv *priv = GET_PRIV (self);
2045 g_return_if_fail (priv->tp_contact != NULL);
2047 tp_contact_request_subscription_async (priv->tp_contact, message,
2048 contact_request_subscription_cb, NULL);
2050 tp_contact_authorize_publication_async (priv->tp_contact,
2051 contact_authorize_publication_cb, NULL);
2053 tp_contact_unblock_async (priv->tp_contact, contact_unblock_cb, NULL);
2056 declare_contact_cb (remove)
2059 empathy_contact_remove_from_contact_list (EmpathyContact *self)
2061 EmpathyContactPriv *priv = GET_PRIV (self);
2063 g_return_if_fail (priv->tp_contact != NULL);
2065 tp_contact_remove_async (priv->tp_contact, contact_remove_cb, NULL);