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