Fix broken markup in Finnish user docs translation
[empathy-mirror.git] / libempathy / empathy-contact.c
blobd2284806f639c48179546d0dcd73ca3f4eb56c2f
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
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>
22 #include "config.h"
23 #include "empathy-contact.h"
25 #include <tp-account-widgets/tpaw-utils.h>
27 #ifdef HAVE_GEOCODE
28 #include <geocode-glib/geocode-glib.h>
29 #endif
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)
39 typedef struct {
40 TpContact *tp_contact;
41 TpAccount *account;
42 FolksPersona *persona;
43 gchar *id;
44 gchar *alias;
45 gchar *logged_alias;
46 EmpathyAvatar *avatar;
47 TpConnectionPresenceType presence;
48 guint handle;
49 EmpathyCapabilities capabilities;
50 gboolean is_user;
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.
58 GHashTable *location;
59 GeeHashSet *groups;
60 gchar **client_types;
61 } EmpathyContactPriv;
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);
69 #ifdef HAVE_GEOCODE
70 static void update_geocode (EmpathyContact *contact);
71 #endif
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,
86 const gchar *token);
88 G_DEFINE_TYPE (EmpathyContact, empathy_contact, G_TYPE_OBJECT);
90 enum
92 PROP_0,
93 PROP_TP_CONTACT,
94 PROP_ACCOUNT,
95 PROP_PERSONA,
96 PROP_ID,
97 PROP_ALIAS,
98 PROP_LOGGED_ALIAS,
99 PROP_AVATAR,
100 PROP_PRESENCE,
101 PROP_PRESENCE_MESSAGE,
102 PROP_HANDLE,
103 PROP_CAPABILITIES,
104 PROP_IS_USER,
105 PROP_LOCATION,
106 PROP_CLIENT_TYPES
109 enum {
110 PRESENCE_CHANGED,
111 LAST_SIGNAL
114 static guint signals[LAST_SIGNAL];
116 /* TpContact* -> EmpathyContact*, both borrowed ref */
117 static GHashTable *contacts_table = NULL;
119 static void
120 tp_contact_notify_cb (TpContact *tp_contact,
121 GParamSpec *param,
122 GObject *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,
134 priv->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));
166 static void
167 folks_persona_notify_cb (FolksPersona *folks_persona,
168 GParamSpec *param,
169 GObject *contact)
171 if (!tp_strdiff (param->name, "presence-message"))
172 g_object_notify (contact, "presence-message");
175 static void
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);
187 if (priv->account)
188 g_object_unref (priv->account);
189 priv->account = NULL;
191 if (priv->persona)
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);
202 priv->avatar = NULL;
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);
214 static void
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)
224 return;
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);
252 static void
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,
266 PROP_TP_CONTACT,
267 g_param_spec_object ("tp-contact",
268 "TpContact",
269 "The TpContact associated with the contact",
270 TP_TYPE_CONTACT,
271 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
273 g_object_class_install_property (object_class,
274 PROP_ACCOUNT,
275 g_param_spec_object ("account",
276 "The account",
277 "The account associated with the contact",
278 TP_TYPE_ACCOUNT,
279 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
281 g_object_class_install_property (object_class,
282 PROP_PERSONA,
283 g_param_spec_object ("persona",
284 "Persona",
285 "The FolksPersona associated with the contact",
286 FOLKS_TYPE_PERSONA,
287 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
289 g_object_class_install_property (object_class,
290 PROP_ID,
291 g_param_spec_string ("id",
292 "Contact id",
293 "String identifying contact",
294 NULL,
295 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
297 g_object_class_install_property (object_class,
298 PROP_ALIAS,
299 g_param_spec_string ("alias",
300 "Contact alias",
301 "An alias for the contact",
302 NULL,
303 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
305 g_object_class_install_property (object_class,
306 PROP_LOGGED_ALIAS,
307 g_param_spec_string ("logged-alias",
308 "Logged alias",
309 "The alias the user had when a message was logged, "
310 "only set when using empathy_contact_from_tpl_contact()",
311 NULL,
312 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
314 g_object_class_install_property (object_class,
315 PROP_AVATAR,
316 g_param_spec_boxed ("avatar",
317 "Avatar image",
318 "The avatar image",
319 EMPATHY_TYPE_AVATAR,
320 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
322 g_object_class_install_property (object_class,
323 PROP_PRESENCE,
324 g_param_spec_uint ("presence",
325 "Contact 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",
337 NULL,
338 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
340 g_object_class_install_property (object_class,
341 PROP_HANDLE,
342 g_param_spec_uint ("handle",
343 "Contact Handle",
344 "The handle of the contact",
346 G_MAXUINT,
348 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
350 g_object_class_install_property (object_class,
351 PROP_CAPABILITIES,
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,
360 PROP_IS_USER,
361 g_param_spec_boolean ("is-user",
362 "Contact is-user",
363 "Is contact the user",
364 FALSE,
365 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
368 g_object_class_install_property (object_class,
369 PROP_LOCATION,
370 g_param_spec_boxed ("location",
371 "Contact location",
372 "Physical location of the contact",
373 G_TYPE_HASH_TABLE,
374 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
376 g_object_class_install_property (object_class,
377 PROP_CLIENT_TYPES,
378 g_param_spec_boxed ("client-types",
379 "Contact client types",
380 "Client types of the contact",
381 G_TYPE_STRV,
382 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
384 signals[PRESENCE_CHANGED] =
385 g_signal_new ("presence-changed",
386 G_TYPE_FROM_CLASS (class),
387 G_SIGNAL_RUN_LAST,
389 NULL, NULL,
390 g_cclosure_marshal_generic,
391 G_TYPE_NONE,
392 2, G_TYPE_UINT,
393 G_TYPE_UINT);
395 g_type_class_add_private (object_class, sizeof (EmpathyContactPriv));
398 static void
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;
408 priv->groups = NULL;
411 static void
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);
423 g_free (priv->id);
424 g_strfreev (priv->client_types);
426 G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object);
429 static void
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)
440 return;
442 priv->capabilities = capabilities;
444 g_object_notify (G_OBJECT (contact), "capabilities");
447 static void
448 empathy_contact_set_id (EmpathyContact *contact,
449 const gchar *id)
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))
463 g_free (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);
474 static void
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)
486 return;
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");
496 static void
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);
511 static void
512 empathy_contact_set_handle (EmpathyContact *contact,
513 guint handle)
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);
530 static void
531 contact_get_property (GObject *object,
532 guint param_id,
533 GValue *value,
534 GParamSpec *pspec)
536 EmpathyContact *contact = EMPATHY_CONTACT (object);
538 switch (param_id)
540 case PROP_TP_CONTACT:
541 g_value_set_object (value, empathy_contact_get_tp_contact (contact));
542 break;
543 case PROP_ACCOUNT:
544 g_value_set_object (value, empathy_contact_get_account (contact));
545 break;
546 case PROP_PERSONA:
547 g_value_set_object (value, empathy_contact_get_persona (contact));
548 break;
549 case PROP_ID:
550 g_value_set_string (value, empathy_contact_get_id (contact));
551 break;
552 case PROP_ALIAS:
553 g_value_set_string (value, empathy_contact_get_alias (contact));
554 break;
555 case PROP_LOGGED_ALIAS:
556 g_value_set_string (value, empathy_contact_get_logged_alias (contact));
557 break;
558 case PROP_AVATAR:
559 g_value_set_boxed (value, empathy_contact_get_avatar (contact));
560 break;
561 case PROP_PRESENCE:
562 g_value_set_uint (value, empathy_contact_get_presence (contact));
563 break;
564 case PROP_PRESENCE_MESSAGE:
565 g_value_set_string (value, empathy_contact_get_presence_message (contact));
566 break;
567 case PROP_HANDLE:
568 g_value_set_uint (value, empathy_contact_get_handle (contact));
569 break;
570 case PROP_CAPABILITIES:
571 g_value_set_flags (value, empathy_contact_get_capabilities (contact));
572 break;
573 case PROP_IS_USER:
574 g_value_set_boolean (value, empathy_contact_is_user (contact));
575 break;
576 default:
577 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
578 break;
582 static void
583 contact_set_property (GObject *object,
584 guint param_id,
585 const GValue *value,
586 GParamSpec *pspec)
588 EmpathyContact *contact = EMPATHY_CONTACT (object);
589 EmpathyContactPriv *priv = GET_PRIV (object);
591 switch (param_id)
593 case PROP_TP_CONTACT:
594 priv->tp_contact = g_value_dup_object (value);
595 break;
596 case PROP_ACCOUNT:
597 g_assert (priv->account == NULL);
598 priv->account = g_value_dup_object (value);
599 break;
600 case PROP_PERSONA:
601 empathy_contact_set_persona (contact, g_value_get_object (value));
602 break;
603 case PROP_ID:
604 empathy_contact_set_id (contact, g_value_get_string (value));
605 break;
606 case PROP_ALIAS:
607 empathy_contact_set_alias (contact, g_value_get_string (value));
608 break;
609 case PROP_LOGGED_ALIAS:
610 g_assert (priv->logged_alias == NULL);
611 priv->logged_alias = g_value_dup_string (value);
612 break;
613 case PROP_PRESENCE:
614 empathy_contact_set_presence (contact, g_value_get_uint (value));
615 break;
616 case PROP_PRESENCE_MESSAGE:
617 empathy_contact_set_presence_message (contact, g_value_get_string (value));
618 break;
619 case PROP_HANDLE:
620 empathy_contact_set_handle (contact, g_value_get_uint (value));
621 break;
622 case PROP_CAPABILITIES:
623 empathy_contact_set_capabilities (contact, g_value_get_flags (value));
624 break;
625 case PROP_IS_USER:
626 empathy_contact_set_is_user (contact, g_value_get_boolean (value));
627 break;
628 default:
629 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
630 break;
634 static void
635 remove_tp_contact (gpointer data,
636 GObject *object)
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,
650 NULL);
652 g_object_weak_ref (G_OBJECT (retval), remove_tp_contact, tp_contact);
654 return retval;
657 typedef struct
659 TplEntity *entity;
660 TpAccount *account;
661 } FindContactData;
663 static gboolean
664 contact_is_tpl_entity (gpointer key,
665 gpointer value,
666 gpointer user_data)
668 EmpathyContact *contact = value;
669 FindContactData *data = user_data;
670 TpAccount *account = empathy_contact_get_account (contact);
671 const gchar *path = NULL;
673 if (account != 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);
681 static void
682 get_contacts_cb (GObject *source,
683 GAsyncResult *result,
684 gpointer user_data)
686 TpWeakRef *wr = user_data;
687 EmpathyContactPriv *priv;
688 EmpathyContact *self;
690 self = tp_weak_ref_dup_object (wr);
691 if (self == NULL)
692 goto out;
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)
701 goto out;
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));
709 out:
710 g_clear_object (&self);
711 tp_weak_ref_destroy (wr);
714 EmpathyContact *
715 empathy_contact_from_tpl_contact (TpAccount *account,
716 TplEntity *tpl_entity)
718 EmpathyContact *retval;
719 gboolean is_user;
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),
740 NULL);
742 else
744 TpConnection *conn;
745 const gchar *id;
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,
752 "id", id,
753 "alias", tpl_entity_get_alias (tpl_entity),
754 "account", account,
755 "is-user", is_user,
756 NULL);
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);
762 if (conn != NULL)
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));
777 return retval;
780 TpContact *
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;
792 const gchar *
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);
804 return priv->id;
807 const gchar *
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))
818 alias = priv->alias;
819 else if (priv->tp_contact != NULL)
820 alias = tp_contact_get_alias (priv->tp_contact);
822 if (!TPAW_STR_EMPTY (alias))
823 return alias;
824 else
825 return empathy_contact_get_id (contact);
828 const gchar *
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;
839 else
840 return empathy_contact_get_alias (contact);
843 void
844 empathy_contact_set_alias (EmpathyContact *contact,
845 const gchar *alias)
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);
876 static void
877 groups_change_group_cb (GObject *source,
878 GAsyncResult *result,
879 gpointer user_data)
881 FolksGroupDetails *group_details = FOLKS_GROUP_DETAILS (source);
882 GError *error = NULL;
884 folks_group_details_change_group_finish (group_details, result, &error);
885 if (error != NULL)
887 g_warning ("failed to change group: %s", error->message);
888 g_clear_error (&error);
892 void
893 empathy_contact_change_group (EmpathyContact *contact, const gchar *group,
894 gboolean is_member)
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);
906 if (persona != NULL)
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);
911 return;
914 /* If the persona doesn't exist yet, we have to cache the changes until it
915 * does */
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);
925 EmpathyAvatar *
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);
934 return priv->avatar;
937 static void
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)
948 return;
950 if (priv->avatar)
952 empathy_avatar_unref (priv->avatar);
953 priv->avatar = NULL;
956 if (avatar)
957 priv->avatar = empathy_avatar_ref (avatar);
959 g_object_notify (G_OBJECT (contact), "avatar");
962 TpAccount *
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);
977 priv->account =
978 g_object_ref (tp_connection_get_account (connection));
981 return priv->account;
984 FolksPersona *
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)
995 TpfPersona *persona;
997 persona = tpf_persona_dup_for_contact (priv->tp_contact);
998 if (persona != NULL)
1000 empathy_contact_set_persona (contact, (FolksPersona *) persona);
1001 g_object_unref (persona);
1005 return priv->persona;
1008 void
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)
1020 return;
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;
1051 TpConnection *
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);
1063 return NULL;
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;
1082 const gchar *
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);
1098 return NULL;
1101 guint
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;
1116 EmpathyCapabilities
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;
1128 gboolean
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;
1140 void
1141 empathy_contact_set_is_user (EmpathyContact *contact,
1142 gboolean is_user)
1144 EmpathyContactPriv *priv;
1146 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1148 priv = GET_PRIV (contact);
1150 if (priv->is_user == is_user)
1151 return;
1153 priv->is_user = is_user;
1155 g_object_notify (G_OBJECT (contact), "is-user");
1158 gboolean
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:
1168 return FALSE;
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:
1177 default:
1178 return TRUE;
1182 const gchar *
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))
1191 return message;
1193 return empathy_presence_get_default_message (
1194 empathy_contact_get_presence (contact));
1197 gboolean
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;
1209 gboolean
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);
1222 gboolean
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;
1234 gboolean
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;
1246 gboolean
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;
1258 gboolean
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;
1270 static gboolean
1271 contact_has_log (EmpathyContact *contact)
1273 TplLogManager *manager;
1274 TplEntity *entity;
1275 gboolean have_log;
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);
1287 return have_log;
1290 gboolean
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:
1299 sensitivity = TRUE;
1300 break;
1301 case EMPATHY_ACTION_SMS:
1302 sensitivity = empathy_contact_can_sms (self);
1303 break;
1304 case EMPATHY_ACTION_AUDIO_CALL:
1305 sensitivity = empathy_contact_can_voip_audio (self);
1306 break;
1307 case EMPATHY_ACTION_VIDEO_CALL:
1308 sensitivity = empathy_contact_can_voip_video (self);
1309 break;
1310 case EMPATHY_ACTION_VIEW_LOGS:
1311 sensitivity = contact_has_log (self);
1312 break;
1313 case EMPATHY_ACTION_SEND_FILE:
1314 sensitivity = empathy_contact_can_send_files (self);
1315 break;
1316 case EMPATHY_ACTION_SHARE_MY_DESKTOP:
1317 sensitivity = empathy_contact_can_use_rfb_stream_tube (self);
1318 break;
1319 default:
1320 g_assert_not_reached ();
1323 return (sensitivity ? TRUE : FALSE);
1326 static gchar *
1327 contact_get_avatar_filename (EmpathyContact *contact,
1328 const gchar *token)
1330 TpAccount *account;
1331 gchar *avatar_path;
1332 gchar *avatar_file;
1333 gchar *token_escaped;
1335 if (TPAW_STR_EMPTY (empathy_contact_get_id (contact)))
1336 return NULL;
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 (),
1342 "telepathy",
1343 "avatars",
1344 tp_account_get_cm_name (account),
1345 tp_account_get_protocol_name (account),
1346 NULL);
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);
1354 return avatar_file;
1357 static gboolean
1358 contact_load_avatar_cache (EmpathyContact *contact,
1359 const gchar *token)
1361 EmpathyAvatar *avatar = NULL;
1362 gchar *filename;
1363 gchar *data = NULL;
1364 gsize len;
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);
1382 if (data != NULL)
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);
1390 g_free (data);
1391 g_free (filename);
1393 return data != NULL;
1396 GType
1397 empathy_avatar_get_type (void)
1399 static GType type_id = 0;
1401 if (!type_id)
1403 type_id = g_boxed_type_register_static ("EmpathyAvatar",
1404 (GBoxedCopyFunc) empathy_avatar_ref,
1405 (GBoxedFreeFunc) empathy_avatar_unref);
1408 return type_id;
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
1422 EmpathyAvatar *
1423 empathy_avatar_new (const guchar *data,
1424 gsize len,
1425 const gchar *format,
1426 const gchar *filename)
1428 EmpathyAvatar *avatar;
1430 avatar = g_slice_new0 (EmpathyAvatar);
1431 avatar->data = g_memdup (data, len);
1432 avatar->len = len;
1433 avatar->format = g_strdup (format);
1434 avatar->filename = g_strdup (filename);
1435 avatar->refcount = 1;
1437 return avatar;
1440 void
1441 empathy_avatar_unref (EmpathyAvatar *avatar)
1443 g_return_if_fail (avatar != NULL);
1445 avatar->refcount--;
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);
1455 EmpathyAvatar *
1456 empathy_avatar_ref (EmpathyAvatar *avatar)
1458 g_return_val_if_fail (avatar != NULL, NULL);
1460 avatar->refcount++;
1462 return avatar;
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
1475 gboolean
1476 empathy_avatar_save_to_file (EmpathyAvatar *self,
1477 const gchar *filename,
1478 GError **error)
1480 return g_file_set_contents (filename, (const gchar *) self->data, self->len,
1481 error);
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
1500 GHashTable *
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.
1523 static void
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);
1538 #ifdef HAVE_GEOCODE
1539 update_geocode (contact);
1540 #endif
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;
1556 static void
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.
1581 gboolean
1582 empathy_contact_equal (gconstpointer contact1,
1583 gconstpointer contact2)
1585 EmpathyContact *c1;
1586 EmpathyContact *c2;
1587 const gchar *id1;
1588 const gchar *id2;
1590 if ((contact1 == NULL) != (contact2 == NULL)) {
1591 return FALSE;
1593 if (contact1 == contact2) {
1594 return TRUE;
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)) {
1601 return TRUE;
1603 return FALSE;
1606 #ifdef HAVE_GEOCODE
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
1611 static void
1612 geocode_cb (GObject *source,
1613 GAsyncResult *result,
1614 gpointer user_data)
1616 EmpathyContact *contact = user_data;
1617 EmpathyContactPriv *priv = GET_PRIV (contact);
1618 GError *error = NULL;
1619 GList *res;
1620 GeocodeLocation *loc;
1621 GHashTable *new_location;
1622 GHashTable *resolved = NULL;
1624 if (priv->location == NULL)
1625 goto out;
1627 res = geocode_forward_search_finish (GEOCODE_FORWARD (source), result,
1628 &error);
1630 if (res == NULL)
1632 DEBUG ("Failed to resolve geocode: %s", error->message);
1633 g_error_free (error);
1634 goto out;
1637 loc = res->data;
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),
1642 NULL);
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");
1659 out:
1660 tp_clear_pointer (&resolved, g_hash_table_unref);
1661 g_object_unref (contact);
1664 static void
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)
1673 return;
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)
1678 return;
1680 geocode = geocode_forward_new_for_params (location);
1681 if (geocode == NULL)
1682 return;
1684 geocode_forward_search_async (geocode, NULL, geocode_cb,
1685 g_object_ref (contact));
1687 g_object_unref (geocode);
1689 #endif
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,
1700 "rfb"))
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;
1719 static void
1720 set_capabilities_from_tp_caps (EmpathyContact *self,
1721 TpCapabilities *caps)
1723 EmpathyCapabilities capabilities;
1725 if (caps == NULL)
1726 return;
1728 capabilities = tp_caps_to_capabilities (caps);
1729 empathy_contact_set_capabilities (self, capabilities);
1732 static void
1733 contact_set_avatar_from_tp_contact (EmpathyContact *contact)
1735 EmpathyContactPriv *priv = GET_PRIV (contact);
1736 const gchar *mime;
1737 GFile *file;
1739 mime = tp_contact_get_avatar_mime_type (priv->tp_contact);
1740 file = tp_contact_get_avatar_file (priv->tp_contact);
1742 if (file != NULL)
1744 EmpathyAvatar *avatar;
1745 gchar *data;
1746 gsize len;
1747 gchar *path;
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);
1756 return;
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);
1765 g_free (path);
1766 g_free (data);
1768 else
1770 contact_set_avatar (contact, NULL);
1774 EmpathyContact *
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);
1783 else
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);
1795 else
1797 g_object_ref (contact);
1800 return contact;
1803 static int
1804 presence_cmp_func (EmpathyContact *a,
1805 EmpathyContact *b)
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));
1819 static gint
1820 voip_cmp_func (EmpathyContact *a,
1821 EmpathyContact *b)
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)
1836 return 0;
1837 else if (has_audio_a)
1838 return -1;
1839 else
1840 return 1;
1842 else if (has_video_a)
1843 return -1;
1844 else
1845 return 1;
1848 static gint
1849 ft_cmp_func (EmpathyContact *a,
1850 EmpathyContact *b)
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)
1858 return 0;
1859 else if (can_send_files_a)
1860 return -1;
1861 else
1862 return 1;
1865 static gint
1866 rfb_stream_tube_cmp_func (EmpathyContact *a,
1867 EmpathyContact *b)
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);
1874 if (rfb_a == rfb_b)
1875 return 0;
1876 else if (rfb_a)
1877 return -1;
1878 else
1879 return 1;
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. */
1885 static int
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. */
1899 static gint
1900 chat_sort_func (EmpathyContact *a,
1901 EmpathyContact *b)
1903 gint result;
1905 result = presence_cmp_func (a, b);
1906 if (result != 0)
1907 return result;
1909 /* Prefer individual supporting file transfer */
1910 result = ft_cmp_func (a, b);
1911 if (result != 0)
1912 return result;
1914 /* Check audio/video capabilities */
1915 result = voip_cmp_func (a, b);
1916 if (result != 0)
1917 return result;
1919 /* Check 'Share my destop' feature */
1920 return rfb_stream_tube_cmp_func (a, b);
1923 static GCompareFunc
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:
1936 default:
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()
1954 EmpathyContact *
1955 empathy_contact_dup_best_for_action (FolksIndividual *individual,
1956 EmpathyActionType action_type)
1958 GeeSet *personas;
1959 GeeIterator *iter;
1960 GList *contacts;
1961 EmpathyContact *best_contact = NULL;
1963 /* Build a list of EmpathyContacts that we can sort */
1964 personas = folks_individual_get_personas (individual);
1965 contacts = NULL;
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))
1975 goto while_finish;
1977 tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona));
1978 if (tp_contact == NULL)
1979 goto while_finish;
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
1985 * action. */
1986 if (empathy_contact_can_do_action (contact, action_type))
1987 contacts = g_list_prepend (contacts, g_object_ref (contact));
1989 while_finish:
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) \
2010 static void \
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)
2030 void
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)
2049 void
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);