Update python binding
[empathy.git] / libempathy / empathy-contact.c
blob1cac48513d55969a89a2ef7a6c14ea85caef3346
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /*
3 * Copyright (C) 2004 Imendio AB
4 * Copyright (C) 2007-2008 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Martyn Russell <martyn@imendio.com>
23 * Xavier Claessens <xclaesse@gmail.com>
24 * Sjoerd Simons <sjoerd.simons@collabora.co.uk>
27 #include "config.h"
29 #include <string.h>
31 #include <glib/gi18n-lib.h>
33 #include <telepathy-glib/util.h>
34 #include <libmissioncontrol/mc-enum-types.h>
36 #include "empathy-contact.h"
37 #include "empathy-contact-factory.h"
38 #include "empathy-utils.h"
39 #include "empathy-enum-types.h"
40 #include "empathy-marshal.h"
42 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
43 #include "empathy-debug.h"
45 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContact)
46 typedef struct {
47 EmpathyContactFactory *factory;
48 gchar *id;
49 gchar *name;
50 EmpathyAvatar *avatar;
51 McAccount *account;
52 McPresence presence;
53 gchar *presence_message;
54 guint handle;
55 EmpathyCapabilities capabilities;
56 gboolean is_user;
57 guint hash;
58 EmpathyContactReady ready;
59 GList *ready_callbacks;
60 } EmpathyContactPriv;
62 typedef struct {
63 EmpathyContactReady ready;
64 EmpathyContactReadyCb *callback;
65 gpointer user_data;
66 GDestroyNotify destroy;
67 GObject *weak_object;
68 } ReadyCbData;
70 static void contact_finalize (GObject *object);
71 static void contact_get_property (GObject *object, guint param_id,
72 GValue *value, GParamSpec *pspec);
73 static void contact_set_property (GObject *object, guint param_id,
74 const GValue *value, GParamSpec *pspec);
76 G_DEFINE_TYPE (EmpathyContact, empathy_contact, G_TYPE_OBJECT);
78 enum
80 PROP_0,
81 PROP_ID,
82 PROP_NAME,
83 PROP_AVATAR,
84 PROP_ACCOUNT,
85 PROP_PRESENCE,
86 PROP_PRESENCE_MESSAGE,
87 PROP_HANDLE,
88 PROP_CAPABILITIES,
89 PROP_IS_USER,
90 PROP_READY
93 enum {
94 PRESENCE_CHANGED,
95 LAST_SIGNAL
98 static guint signals[LAST_SIGNAL];
100 static void
101 contact_dispose (GObject *object)
103 EmpathyContactPriv *priv = GET_PRIV (object);
105 if (priv->account)
106 g_object_unref (priv->account);
107 priv->account = NULL;
109 if (priv->factory)
110 g_object_unref (priv->factory);
111 priv->factory = NULL;
113 G_OBJECT_CLASS (empathy_contact_parent_class)->dispose (object);
116 static void
117 empathy_contact_class_init (EmpathyContactClass *class)
119 GObjectClass *object_class;
121 object_class = G_OBJECT_CLASS (class);
123 object_class->finalize = contact_finalize;
124 object_class->dispose = contact_dispose;
125 object_class->get_property = contact_get_property;
126 object_class->set_property = contact_set_property;
128 g_object_class_install_property (object_class,
129 PROP_ID,
130 g_param_spec_string ("id",
131 "Contact id",
132 "String identifying contact",
133 NULL,
134 G_PARAM_READWRITE));
136 g_object_class_install_property (object_class,
137 PROP_NAME,
138 g_param_spec_string ("name",
139 "Contact Name",
140 "The name of the contact",
141 NULL,
142 G_PARAM_READWRITE));
144 g_object_class_install_property (object_class,
145 PROP_AVATAR,
146 g_param_spec_boxed ("avatar",
147 "Avatar image",
148 "The avatar image",
149 EMPATHY_TYPE_AVATAR,
150 G_PARAM_READWRITE));
152 g_object_class_install_property (object_class,
153 PROP_ACCOUNT,
154 g_param_spec_object ("account",
155 "Contact Account",
156 "The account associated with the contact",
157 MC_TYPE_ACCOUNT,
158 G_PARAM_READWRITE));
160 g_object_class_install_property (object_class,
161 PROP_PRESENCE,
162 g_param_spec_uint ("presence",
163 "Contact presence",
164 "Presence of contact",
165 MC_PRESENCE_UNSET,
166 LAST_MC_PRESENCE,
167 MC_PRESENCE_UNSET,
168 G_PARAM_READWRITE));
170 g_object_class_install_property (object_class,
171 PROP_PRESENCE_MESSAGE,
172 g_param_spec_string ("presence-message",
173 "Contact presence message",
174 "Presence message of contact",
175 NULL,
176 G_PARAM_READWRITE));
178 g_object_class_install_property (object_class,
179 PROP_HANDLE,
180 g_param_spec_uint ("handle",
181 "Contact Handle",
182 "The handle of the contact",
184 G_MAXUINT,
186 G_PARAM_READWRITE));
188 g_object_class_install_property (object_class,
189 PROP_CAPABILITIES,
190 g_param_spec_flags ("capabilities",
191 "Contact Capabilities",
192 "Capabilities of the contact",
193 EMPATHY_TYPE_CAPABILITIES,
194 EMPATHY_CAPABILITIES_UNKNOWN,
195 G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
197 g_object_class_install_property (object_class,
198 PROP_IS_USER,
199 g_param_spec_boolean ("is-user",
200 "Contact is-user",
201 "Is contact the user",
202 FALSE,
203 G_PARAM_READWRITE));
205 g_object_class_install_property (object_class,
206 PROP_READY,
207 g_param_spec_flags ("ready",
208 "Contact ready flags",
209 "Flags for ready properties",
210 EMPATHY_TYPE_CONTACT_READY,
211 EMPATHY_CONTACT_READY_NONE,
212 G_PARAM_READABLE));
214 signals[PRESENCE_CHANGED] =
215 g_signal_new ("presence-changed",
216 G_TYPE_FROM_CLASS (class),
217 G_SIGNAL_RUN_LAST,
219 NULL, NULL,
220 _empathy_marshal_VOID__ENUM_ENUM,
221 G_TYPE_NONE,
222 2, MC_TYPE_PRESENCE,
223 MC_TYPE_PRESENCE);
225 g_type_class_add_private (object_class, sizeof (EmpathyContactPriv));
228 static void
229 empathy_contact_init (EmpathyContact *contact)
231 EmpathyContactPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (contact,
232 EMPATHY_TYPE_CONTACT, EmpathyContactPriv);
234 contact->priv = priv;
236 /* Keep a ref to the factory to be sure it is not finalized while there is
237 * still contacts alive. */
238 priv->factory = empathy_contact_factory_dup_singleton ();
241 static void
242 contact_finalize (GObject *object)
244 EmpathyContactPriv *priv;
245 GList *l;
247 priv = GET_PRIV (object);
249 DEBUG ("finalize: %p", object);
251 g_free (priv->name);
252 g_free (priv->id);
253 g_free (priv->presence_message);
255 for (l = priv->ready_callbacks; l != NULL; l = g_list_next (l))
257 ReadyCbData *d = (ReadyCbData *)l->data;
259 if (d->destroy != NULL)
260 d->destroy (d->user_data);
261 g_slice_free (ReadyCbData, d);
264 g_list_free (priv->ready_callbacks);
265 priv->ready_callbacks = NULL;
267 if (priv->avatar)
268 empathy_avatar_unref (priv->avatar);
270 G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object);
273 static void
274 contact_get_property (GObject *object,
275 guint param_id,
276 GValue *value,
277 GParamSpec *pspec)
279 EmpathyContactPriv *priv;
281 priv = GET_PRIV (object);
283 switch (param_id)
285 case PROP_ID:
286 g_value_set_string (value, priv->id);
287 break;
288 case PROP_NAME:
289 g_value_set_string (value,
290 empathy_contact_get_name (EMPATHY_CONTACT (object)));
291 break;
292 case PROP_AVATAR:
293 g_value_set_boxed (value, priv->avatar);
294 break;
295 case PROP_ACCOUNT:
296 g_value_set_object (value, priv->account);
297 break;
298 case PROP_PRESENCE:
299 g_value_set_uint (value, priv->presence);
300 break;
301 case PROP_PRESENCE_MESSAGE:
302 g_value_set_string (value, priv->presence_message);
303 break;
304 case PROP_HANDLE:
305 g_value_set_uint (value, priv->handle);
306 break;
307 case PROP_CAPABILITIES:
308 g_value_set_flags (value, priv->capabilities);
309 break;
310 case PROP_IS_USER:
311 g_value_set_boolean (value, priv->is_user);
312 break;
313 case PROP_READY:
314 g_value_set_flags (value, priv->ready);
315 break;
316 default:
317 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
318 break;
322 static void
323 contact_set_property (GObject *object,
324 guint param_id,
325 const GValue *value,
326 GParamSpec *pspec)
328 EmpathyContactPriv *priv;
330 priv = GET_PRIV (object);
332 switch (param_id)
334 case PROP_ID:
335 empathy_contact_set_id (EMPATHY_CONTACT (object),
336 g_value_get_string (value));
337 break;
338 case PROP_NAME:
339 empathy_contact_set_name (EMPATHY_CONTACT (object),
340 g_value_get_string (value));
341 break;
342 case PROP_AVATAR:
343 empathy_contact_set_avatar (EMPATHY_CONTACT (object),
344 g_value_get_boxed (value));
345 break;
346 case PROP_ACCOUNT:
347 empathy_contact_set_account (EMPATHY_CONTACT (object),
348 MC_ACCOUNT (g_value_get_object (value)));
349 break;
350 case PROP_PRESENCE:
351 empathy_contact_set_presence (EMPATHY_CONTACT (object),
352 g_value_get_uint (value));
353 break;
354 case PROP_PRESENCE_MESSAGE:
355 empathy_contact_set_presence_message (EMPATHY_CONTACT (object),
356 g_value_get_string (value));
357 break;
358 case PROP_HANDLE:
359 empathy_contact_set_handle (EMPATHY_CONTACT (object),
360 g_value_get_uint (value));
361 break;
362 case PROP_CAPABILITIES:
363 empathy_contact_set_capabilities (EMPATHY_CONTACT (object),
364 g_value_get_flags (value));
365 break;
366 case PROP_IS_USER:
367 empathy_contact_set_is_user (EMPATHY_CONTACT (object),
368 g_value_get_boolean (value));
369 break;
370 default:
371 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
372 break;
376 static gboolean
377 contact_is_ready (EmpathyContact *contact, EmpathyContactReady ready)
379 EmpathyContactPriv *priv = GET_PRIV (contact);
381 /* When the name is NULL, empathy_contact_get_name() fallback to the id.
382 * When the caller want to wait the name to be ready, it also want to wait
383 * the id to be ready in case of fallback. */
384 if ((ready & EMPATHY_CONTACT_READY_NAME) && EMP_STR_EMPTY (priv->name))
385 ready |= EMPATHY_CONTACT_READY_ID;
387 return (priv->ready & ready) == ready;
390 static void
391 contact_weak_object_notify (gpointer data, GObject *old_object)
393 EmpathyContact *contact = EMPATHY_CONTACT (data);
394 EmpathyContactPriv *priv = GET_PRIV (contact);
396 GList *l, *ln;
398 for (l = priv->ready_callbacks ; l != NULL ; l = ln )
400 ReadyCbData *d = (ReadyCbData *)l->data;
401 ln = g_list_next (l);
403 if (d->weak_object == old_object)
405 if (d->destroy != NULL)
406 d->destroy (d->user_data);
408 priv->ready_callbacks = g_list_delete_link (priv->ready_callbacks,
411 g_slice_free (ReadyCbData, d);
416 static void
417 contact_call_ready_callback (EmpathyContact *contact, const GError *error,
418 ReadyCbData *data)
420 data->callback (contact, error, data->user_data, data->weak_object);
421 if (data->destroy != NULL)
422 data->destroy (data->user_data);
424 if (data->weak_object)
425 g_object_weak_unref (data->weak_object,
426 contact_weak_object_notify, contact);
430 static void
431 contact_set_ready_flag (EmpathyContact *contact,
432 EmpathyContactReady flag)
434 EmpathyContactPriv *priv = GET_PRIV (contact);
436 if (!(priv->ready & flag))
438 GList *l, *ln;
440 priv->ready |= flag;
441 g_object_notify (G_OBJECT (contact), "ready");
443 for (l = priv->ready_callbacks ; l != NULL ; l = ln )
445 ReadyCbData *d = (ReadyCbData *)l->data;
446 ln = g_list_next (l);
448 if (contact_is_ready (contact, d->ready))
450 contact_call_ready_callback (contact, NULL, d);
451 priv->ready_callbacks = g_list_delete_link
452 (priv->ready_callbacks, l);
453 g_slice_free (ReadyCbData, d);
459 static void
460 contact_remove_ready_flag (EmpathyContact *contact,
461 EmpathyContactReady flag)
463 EmpathyContactPriv *priv = GET_PRIV (contact);
465 if (priv->ready & flag)
467 priv->ready ^= flag;
468 g_object_notify (G_OBJECT (contact), "ready");
472 EmpathyContact *
473 empathy_contact_new (McAccount *account)
475 return g_object_new (EMPATHY_TYPE_CONTACT,
476 "account", account,
477 NULL);
480 EmpathyContact *
481 empathy_contact_new_full (McAccount *account,
482 const gchar *id,
483 const gchar *name)
485 return g_object_new (EMPATHY_TYPE_CONTACT,
486 "account", account,
487 "name", name,
488 "id", id,
489 NULL);
492 const gchar *
493 empathy_contact_get_id (EmpathyContact *contact)
495 EmpathyContactPriv *priv;
497 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
499 priv = GET_PRIV (contact);
501 return priv->id;
504 void
505 empathy_contact_set_id (EmpathyContact *contact,
506 const gchar *id)
508 EmpathyContactPriv *priv;
510 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
511 g_return_if_fail (id != NULL);
513 priv = GET_PRIV (contact);
515 /* We temporally ref the contact because it could be destroyed
516 * during the signal emition */
517 g_object_ref (contact);
518 if (tp_strdiff (id, priv->id))
520 g_free (priv->id);
521 priv->id = g_strdup (id);
523 g_object_notify (G_OBJECT (contact), "id");
524 if (EMP_STR_EMPTY (priv->name))
525 g_object_notify (G_OBJECT (contact), "name");
527 contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_ID);
529 g_object_unref (contact);
532 const gchar *
533 empathy_contact_get_name (EmpathyContact *contact)
535 EmpathyContactPriv *priv;
537 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
539 priv = GET_PRIV (contact);
541 if (EMP_STR_EMPTY (priv->name))
542 return empathy_contact_get_id (contact);
544 return priv->name;
547 void
548 empathy_contact_set_name (EmpathyContact *contact,
549 const gchar *name)
551 EmpathyContactPriv *priv;
553 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
555 priv = GET_PRIV (contact);
557 g_object_ref (contact);
558 if (tp_strdiff (name, priv->name))
560 g_free (priv->name);
561 priv->name = g_strdup (name);
562 g_object_notify (G_OBJECT (contact), "name");
564 contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_NAME);
565 g_object_unref (contact);
568 EmpathyAvatar *
569 empathy_contact_get_avatar (EmpathyContact *contact)
571 EmpathyContactPriv *priv;
573 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
575 priv = GET_PRIV (contact);
577 return priv->avatar;
580 void
581 empathy_contact_set_avatar (EmpathyContact *contact,
582 EmpathyAvatar *avatar)
584 EmpathyContactPriv *priv;
586 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
588 priv = GET_PRIV (contact);
590 if (priv->avatar == avatar)
591 return;
593 if (priv->avatar)
595 empathy_avatar_unref (priv->avatar);
596 priv->avatar = NULL;
599 if (avatar)
600 priv->avatar = empathy_avatar_ref (avatar);
602 g_object_notify (G_OBJECT (contact), "avatar");
605 McAccount *
606 empathy_contact_get_account (EmpathyContact *contact)
608 EmpathyContactPriv *priv;
610 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
612 priv = GET_PRIV (contact);
614 return priv->account;
617 void
618 empathy_contact_set_account (EmpathyContact *contact,
619 McAccount *account)
621 EmpathyContactPriv *priv;
623 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
624 g_return_if_fail (MC_IS_ACCOUNT (account));
626 priv = GET_PRIV (contact);
628 if (account == priv->account)
629 return;
631 if (priv->account)
632 g_object_unref (priv->account);
633 priv->account = g_object_ref (account);
635 g_object_notify (G_OBJECT (contact), "account");
638 McPresence
639 empathy_contact_get_presence (EmpathyContact *contact)
641 EmpathyContactPriv *priv;
643 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), MC_PRESENCE_UNSET);
645 priv = GET_PRIV (contact);
647 return priv->presence;
650 void
651 empathy_contact_set_presence (EmpathyContact *contact,
652 McPresence presence)
654 EmpathyContactPriv *priv;
655 McPresence old_presence;
657 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
659 priv = GET_PRIV (contact);
661 if (presence == priv->presence)
662 return;
664 old_presence = priv->presence;
665 priv->presence = presence;
667 g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence, old_presence);
669 g_object_notify (G_OBJECT (contact), "presence");
672 const gchar *
673 empathy_contact_get_presence_message (EmpathyContact *contact)
675 EmpathyContactPriv *priv;
677 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
679 priv = GET_PRIV (contact);
681 return priv->presence_message;
684 void
685 empathy_contact_set_presence_message (EmpathyContact *contact,
686 const gchar *message)
688 EmpathyContactPriv *priv = GET_PRIV (contact);
690 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
692 if (!tp_strdiff (message, priv->presence_message))
693 return;
695 g_free (priv->presence_message);
696 priv->presence_message = g_strdup (message);
698 g_object_notify (G_OBJECT (contact), "presence-message");
701 guint
702 empathy_contact_get_handle (EmpathyContact *contact)
704 EmpathyContactPriv *priv;
706 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
708 priv = GET_PRIV (contact);
710 return priv->handle;
713 void
714 empathy_contact_set_handle (EmpathyContact *contact,
715 guint handle)
717 EmpathyContactPriv *priv;
719 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
721 priv = GET_PRIV (contact);
723 g_object_ref (contact);
724 if (handle != priv->handle)
726 priv->handle = handle;
727 g_object_notify (G_OBJECT (contact), "handle");
730 if (handle != 0)
731 contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_HANDLE);
732 else
733 contact_remove_ready_flag (contact, EMPATHY_CONTACT_READY_HANDLE);
735 g_object_unref (contact);
738 EmpathyCapabilities
739 empathy_contact_get_capabilities (EmpathyContact *contact)
741 EmpathyContactPriv *priv;
743 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
745 priv = GET_PRIV (contact);
747 return priv->capabilities;
750 void
751 empathy_contact_set_capabilities (EmpathyContact *contact,
752 EmpathyCapabilities capabilities)
754 EmpathyContactPriv *priv;
756 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
758 priv = GET_PRIV (contact);
760 if (priv->capabilities == capabilities)
761 return;
763 priv->capabilities = capabilities;
765 g_object_notify (G_OBJECT (contact), "capabilities");
768 gboolean
769 empathy_contact_is_user (EmpathyContact *contact)
771 EmpathyContactPriv *priv;
773 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
775 priv = GET_PRIV (contact);
777 return priv->is_user;
780 void
781 empathy_contact_set_is_user (EmpathyContact *contact,
782 gboolean is_user)
784 EmpathyContactPriv *priv;
786 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
788 priv = GET_PRIV (contact);
790 if (priv->is_user == is_user)
791 return;
793 priv->is_user = is_user;
795 g_object_notify (G_OBJECT (contact), "is-user");
798 gboolean
799 empathy_contact_is_online (EmpathyContact *contact)
801 EmpathyContactPriv *priv;
803 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
805 priv = GET_PRIV (contact);
807 return (priv->presence > MC_PRESENCE_OFFLINE);
810 const gchar *
811 empathy_contact_get_status (EmpathyContact *contact)
813 EmpathyContactPriv *priv;
815 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), "");
817 priv = GET_PRIV (contact);
819 if (priv->presence_message)
820 return priv->presence_message;
822 return empathy_presence_get_default_message (priv->presence);
825 gboolean
826 empathy_contact_can_voip (EmpathyContact *contact)
828 EmpathyContactPriv *priv;
830 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
832 priv = GET_PRIV (contact);
834 return priv->capabilities & (EMPATHY_CAPABILITIES_AUDIO |
835 EMPATHY_CAPABILITIES_VIDEO);
838 gboolean
839 empathy_contact_can_send_files (EmpathyContact *contact)
841 EmpathyContactPriv *priv;
843 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
845 priv = GET_PRIV (contact);
847 return priv->capabilities & EMPATHY_CAPABILITIES_FT;
850 EmpathyContactReady
851 empathy_contact_get_ready (EmpathyContact *contact)
853 EmpathyContactPriv *priv;
855 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
857 priv = GET_PRIV (contact);
859 return priv->ready;
862 gboolean
863 empathy_contact_equal (gconstpointer v1,
864 gconstpointer v2)
866 McAccount *account_a;
867 McAccount *account_b;
868 const gchar *id_a;
869 const gchar *id_b;
871 g_return_val_if_fail (EMPATHY_IS_CONTACT (v1), FALSE);
872 g_return_val_if_fail (EMPATHY_IS_CONTACT (v2), FALSE);
874 account_a = empathy_contact_get_account (EMPATHY_CONTACT (v1));
875 account_b = empathy_contact_get_account (EMPATHY_CONTACT (v2));
877 id_a = empathy_contact_get_id (EMPATHY_CONTACT (v1));
878 id_b = empathy_contact_get_id (EMPATHY_CONTACT (v2));
880 return empathy_account_equal (account_a, account_b) &&
881 !tp_strdiff (id_a, id_b);
884 guint
885 empathy_contact_hash (gconstpointer key)
887 EmpathyContactPriv *priv;
889 g_return_val_if_fail (EMPATHY_IS_CONTACT (key), +1);
891 priv = GET_PRIV (EMPATHY_CONTACT (key));
893 if (priv->hash == 0)
895 priv->hash = empathy_account_hash (priv->account) ^
896 g_str_hash (priv->id);
899 return priv->hash;
902 void empathy_contact_call_when_ready (EmpathyContact *contact,
903 EmpathyContactReady ready, EmpathyContactReadyCb *callback,
904 gpointer user_data, GDestroyNotify destroy, GObject *weak_object)
906 EmpathyContactPriv *priv = GET_PRIV (contact);
908 g_return_if_fail (contact != NULL);
909 g_return_if_fail (callback != NULL);
911 if (contact_is_ready (contact, ready))
913 callback (contact, NULL, user_data, weak_object);
914 if (destroy != NULL)
915 destroy (user_data);
917 else
919 ReadyCbData *d = g_slice_new0 (ReadyCbData);
920 d->ready = ready;
921 d->callback = callback;
922 d->user_data = user_data;
923 d->destroy = destroy;
924 d->weak_object = weak_object;
926 if (weak_object != NULL)
927 g_object_weak_ref (weak_object, contact_weak_object_notify, contact);
929 priv->ready_callbacks = g_list_prepend (priv->ready_callbacks, d);
933 static gboolean
934 contact_is_ready_func (GObject *contact,
935 gpointer user_data)
937 return contact_is_ready (EMPATHY_CONTACT (contact),
938 GPOINTER_TO_UINT (user_data));
941 void
942 empathy_contact_run_until_ready (EmpathyContact *contact,
943 EmpathyContactReady ready,
944 GMainLoop **loop)
946 empathy_run_until_ready_full (contact, "notify::ready",
947 contact_is_ready_func, GUINT_TO_POINTER (ready),
948 loop);
951 static gchar *
952 contact_get_avatar_filename (EmpathyContact *contact,
953 const gchar *token)
955 EmpathyContactPriv *priv = GET_PRIV (contact);
956 gchar *avatar_path;
957 gchar *avatar_file;
958 gchar *token_escaped;
959 gchar *contact_escaped;
961 if (EMP_STR_EMPTY (priv->id))
962 return NULL;
964 contact_escaped = tp_escape_as_identifier (priv->id);
965 token_escaped = tp_escape_as_identifier (token);
967 avatar_path = g_build_filename (g_get_user_cache_dir (),
968 PACKAGE_NAME,
969 "avatars",
970 mc_account_get_unique_name (priv->account),
971 contact_escaped,
972 NULL);
973 g_mkdir_with_parents (avatar_path, 0700);
975 avatar_file = g_build_filename (avatar_path, token_escaped, NULL);
977 g_free (contact_escaped);
978 g_free (token_escaped);
979 g_free (avatar_path);
981 return avatar_file;
984 void
985 empathy_contact_load_avatar_data (EmpathyContact *contact,
986 const guchar *data,
987 const gsize len,
988 const gchar *format,
989 const gchar *token)
991 EmpathyAvatar *avatar;
992 gchar *filename;
993 GError *error = NULL;
995 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
996 g_return_if_fail (data != NULL);
997 g_return_if_fail (len > 0);
998 g_return_if_fail (format != NULL);
999 g_return_if_fail (!EMP_STR_EMPTY (token));
1001 /* Load and set the avatar */
1002 avatar = empathy_avatar_new (g_memdup (data, len), len, g_strdup (format),
1003 g_strdup (token));
1004 empathy_contact_set_avatar (contact, avatar);
1005 empathy_avatar_unref (avatar);
1007 /* Save to cache if not yet in it */
1008 filename = contact_get_avatar_filename (contact, token);
1009 if (filename && !g_file_test (filename, G_FILE_TEST_EXISTS))
1011 if (!empathy_avatar_save_to_file (avatar, filename, &error))
1013 DEBUG ("Failed to save avatar in cache: %s",
1014 error ? error->message : "No error given");
1015 g_clear_error (&error);
1017 else
1018 DEBUG ("Avatar saved to %s", filename);
1020 g_free (filename);
1023 gboolean
1024 empathy_contact_load_avatar_cache (EmpathyContact *contact,
1025 const gchar *token)
1027 EmpathyAvatar *avatar = NULL;
1028 gchar *filename;
1029 gchar *data = NULL;
1030 gsize len;
1031 GError *error = NULL;
1033 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1034 g_return_val_if_fail (!EMP_STR_EMPTY (token), FALSE);
1036 /* Load the avatar from file if it exists */
1037 filename = contact_get_avatar_filename (contact, token);
1038 if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
1040 if (!g_file_get_contents (filename, &data, &len, &error))
1042 DEBUG ("Failed to load avatar from cache: %s",
1043 error ? error->message : "No error given");
1044 g_clear_error (&error);
1048 if (data)
1050 DEBUG ("Avatar loaded from %s", filename);
1051 avatar = empathy_avatar_new (data, len, NULL, g_strdup (token));
1052 empathy_contact_set_avatar (contact, avatar);
1053 empathy_avatar_unref (avatar);
1056 g_free (filename);
1058 return data != NULL;
1061 GType
1062 empathy_avatar_get_type (void)
1064 static GType type_id = 0;
1066 if (!type_id)
1068 type_id = g_boxed_type_register_static ("EmpathyAvatar",
1069 (GBoxedCopyFunc) empathy_avatar_ref,
1070 (GBoxedFreeFunc) empathy_avatar_unref);
1073 return type_id;
1076 EmpathyAvatar *
1077 empathy_avatar_new (guchar *data,
1078 gsize len,
1079 gchar *format,
1080 gchar *token)
1082 EmpathyAvatar *avatar;
1084 avatar = g_slice_new0 (EmpathyAvatar);
1085 avatar->data = data;
1086 avatar->len = len;
1087 avatar->format = format;
1088 avatar->token = token;
1089 avatar->refcount = 1;
1091 return avatar;
1094 void
1095 empathy_avatar_unref (EmpathyAvatar *avatar)
1097 g_return_if_fail (avatar != NULL);
1099 avatar->refcount--;
1100 if (avatar->refcount == 0)
1102 g_free (avatar->data);
1103 g_free (avatar->format);
1104 g_free (avatar->token);
1105 g_slice_free (EmpathyAvatar, avatar);
1109 EmpathyAvatar *
1110 empathy_avatar_ref (EmpathyAvatar *avatar)
1112 g_return_val_if_fail (avatar != NULL, NULL);
1114 avatar->refcount++;
1116 return avatar;
1120 * empathy_avatar_save_to_file:
1121 * @avatar: the avatar
1122 * @filename: name of a file to write avatar to
1123 * @error: return location for a GError, or NULL
1125 * Save the avatar to a file named filename
1127 * Returns: %TRUE on success, %FALSE if an error occurred
1129 gboolean
1130 empathy_avatar_save_to_file (EmpathyAvatar *self,
1131 const gchar *filename,
1132 GError **error)
1134 return g_file_set_contents (filename, self->data, self->len, error);