]> git.0d.be Git - empathy.git/blob - libempathy/gossip-contact.c
[darcs-to-svn @ Updating TODO]
[empathy.git] / libempathy / gossip-contact.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2004-2007 Imendio AB
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program 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  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Mikael Hallendal <micke@imendio.com>
21  *          Martyn Russell <martyn@imendio.com>
22  */
23
24 #include "config.h"
25
26 #include <string.h>
27
28 #include <glib/gi18n.h>
29
30 #include "gossip-contact.h"
31 #include "gossip-utils.h"
32 #include "gossip-debug.h"
33
34 #define DEBUG_DOMAIN "Contact"
35
36 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CONTACT, GossipContactPriv))
37
38 typedef struct _GossipContactPriv GossipContactPriv;
39
40 struct _GossipContactPriv {
41         gchar              *id;
42         gchar              *name;
43         guint               handle;
44         GList              *presences;
45         GList              *groups;
46         GossipSubscription  subscription;
47         GossipAvatar       *avatar;
48         McAccount          *account;
49 };
50
51 static void contact_class_init    (GossipContactClass *class);
52 static void contact_init          (GossipContact      *contact);
53 static void contact_finalize      (GObject            *object);
54 static void contact_get_property  (GObject            *object,
55                                    guint               param_id,
56                                    GValue             *value,
57                                    GParamSpec         *pspec);
58 static void contact_set_property  (GObject            *object,
59                                    guint               param_id,
60                                    const GValue       *value,
61                                    GParamSpec         *pspec);
62 static void contact_set_presences (GossipContact      *contact,
63                                    GList              *presences);
64
65 enum {
66         PROP_0,
67         PROP_NAME,
68         PROP_ID,
69         PROP_PRESENCES,
70         PROP_GROUPS,
71         PROP_SUBSCRIPTION,
72         PROP_AVATAR,
73         PROP_HANDLE,
74         PROP_ACCOUNT
75 };
76
77 static gpointer parent_class = NULL;
78
79 GType
80 gossip_contact_get_gtype (void)
81 {
82         static GType type = 0;
83
84         if (!type) {
85                 static const GTypeInfo info = {
86                         sizeof (GossipContactClass),
87                         NULL, /* base_init */
88                         NULL, /* base_finalize */
89                         (GClassInitFunc) contact_class_init,
90                         NULL, /* class_finalize */
91                         NULL, /* class_data */
92                         sizeof (GossipContact),
93                         0,    /* n_preallocs */
94                         (GInstanceInitFunc) contact_init
95                 };
96
97                 type = g_type_register_static (G_TYPE_OBJECT,
98                                                "GossipContact",
99                                                &info, 0);
100         }
101
102         return type;
103 }
104
105 static void
106 contact_class_init (GossipContactClass *class)
107 {
108         GObjectClass *object_class;
109
110         object_class = G_OBJECT_CLASS (class);
111         parent_class = g_type_class_peek_parent (class);
112
113         object_class->finalize     = contact_finalize;
114         object_class->get_property = contact_get_property;
115         object_class->set_property = contact_set_property;
116
117         g_object_class_install_property (object_class,
118                                          PROP_NAME,
119                                          g_param_spec_string ("name",
120                                                               "Contact Name",
121                                                               "The name of the contact",
122                                                               NULL,
123                                                               G_PARAM_READWRITE));
124
125         g_object_class_install_property (object_class,
126                                          PROP_ID,
127                                          g_param_spec_string ("id",
128                                                               "Contact id",
129                                                               "String identifying contact",
130                                                               NULL,
131                                                               G_PARAM_READWRITE));
132
133         g_object_class_install_property (object_class,
134                                          PROP_PRESENCES,
135                                          g_param_spec_pointer ("presences",
136                                                                "Contact presences",
137                                                                "Presences of contact",
138                                                                G_PARAM_READWRITE));
139
140         g_object_class_install_property (object_class,
141                                          PROP_GROUPS,
142                                          g_param_spec_pointer ("groups",
143                                                                "Contact groups",
144                                                                "Groups of contact",
145                                                                G_PARAM_READWRITE));
146
147         g_object_class_install_property (object_class,
148                                          PROP_SUBSCRIPTION,
149                                          g_param_spec_int ("subscription",
150                                                            "Contact Subscription",
151                                                            "The subscription status of the contact",
152                                                            GOSSIP_SUBSCRIPTION_NONE,
153                                                            GOSSIP_SUBSCRIPTION_BOTH,
154                                                            GOSSIP_SUBSCRIPTION_NONE,
155                                                            G_PARAM_READWRITE));
156
157         g_object_class_install_property (object_class,
158                                          PROP_AVATAR,
159                                          g_param_spec_boxed ("avatar",
160                                                              "Avatar image",
161                                                              "The avatar image",
162                                                              GOSSIP_TYPE_AVATAR,
163                                                              G_PARAM_READWRITE));
164
165         g_object_class_install_property (object_class,
166                                          PROP_ACCOUNT,
167                                          g_param_spec_object ("account",
168                                                               "Contact Account",
169                                                               "The account associated with the contact",
170                                                               MC_TYPE_ACCOUNT,
171                                                               G_PARAM_READWRITE));
172
173         g_object_class_install_property (object_class,
174                                          PROP_HANDLE,
175                                          g_param_spec_uint ("handle",
176                                                             "Contact Handle",
177                                                             "The handle of the contact",
178                                                             0,
179                                                             G_MAXUINT,
180                                                             0,
181                                                             G_PARAM_READWRITE));
182
183         g_type_class_add_private (object_class, sizeof (GossipContactPriv));
184 }
185
186 static void
187 contact_init (GossipContact *contact)
188 {
189         GossipContactPriv *priv;
190
191         priv = GET_PRIV (contact);
192
193         priv->name        = NULL;
194         priv->id          = NULL;
195         priv->presences   = NULL;
196         priv->account     = NULL;
197         priv->groups      = NULL;
198         priv->avatar      = NULL;
199         priv->handle      = 0;
200 }
201
202 static void
203 contact_finalize (GObject *object)
204 {
205         GossipContactPriv *priv;
206
207         priv = GET_PRIV (object);
208
209         gossip_debug (DEBUG_DOMAIN, "finalize: %p", object);
210
211         g_free (priv->name);
212         g_free (priv->id);
213
214         if (priv->avatar) {
215                 gossip_avatar_unref (priv->avatar);
216         }
217
218         if (priv->presences) {
219                 g_list_foreach (priv->presences, (GFunc) g_object_unref, NULL);
220                 g_list_free (priv->presences);
221         }
222
223         if (priv->groups) {
224                 g_list_foreach (priv->groups, (GFunc) g_free, NULL);
225                 g_list_free (priv->groups);
226         }
227
228         if (priv->account) {
229                 g_object_unref (priv->account);
230         }
231
232         (G_OBJECT_CLASS (parent_class)->finalize) (object);
233 }
234
235 static void
236 contact_get_property (GObject    *object,
237                       guint       param_id,
238                       GValue     *value,
239                       GParamSpec *pspec)
240 {
241         GossipContactPriv *priv;
242
243         priv = GET_PRIV (object);
244
245         switch (param_id) {
246         case PROP_NAME:
247                 g_value_set_string (value,
248                                     gossip_contact_get_name (GOSSIP_CONTACT (object)));
249                 break;
250         case PROP_ID:
251                 g_value_set_string (value,
252                                     gossip_contact_get_id (GOSSIP_CONTACT (object)));
253                 break;
254         case PROP_PRESENCES:
255                 g_value_set_pointer (value, priv->presences);
256                 break;
257         case PROP_GROUPS:
258                 g_value_set_pointer (value, priv->groups);
259                 break;
260         case PROP_SUBSCRIPTION:
261                 g_value_set_int (value, priv->subscription);
262                 break;
263         case PROP_AVATAR:
264                 g_value_set_boxed (value, priv->avatar);
265                 break;
266         case PROP_ACCOUNT:
267                 g_value_set_object (value, priv->account);
268                 break;
269         case PROP_HANDLE:
270                 g_value_set_uint (value, priv->handle);
271                 break;
272         default:
273                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
274                 break;
275         };
276 }
277
278 static void
279 contact_set_property (GObject      *object,
280                       guint         param_id,
281                       const GValue *value,
282                       GParamSpec   *pspec)
283 {
284         GossipContactPriv *priv;
285
286         priv = GET_PRIV (object);
287
288         switch (param_id) {
289         case PROP_NAME:
290                 gossip_contact_set_name (GOSSIP_CONTACT (object),
291                                          g_value_get_string (value));
292                 break;
293         case PROP_ID:
294                 gossip_contact_set_id (GOSSIP_CONTACT (object),
295                                        g_value_get_string (value));
296                 break;
297         case PROP_PRESENCES:
298                 contact_set_presences (GOSSIP_CONTACT (object),
299                                        g_value_get_pointer (value));
300                 break;
301         case PROP_GROUPS:
302                 gossip_contact_set_groups (GOSSIP_CONTACT (object),
303                                            g_value_get_pointer (value));
304                 break;
305         case PROP_SUBSCRIPTION:
306                 gossip_contact_set_subscription (GOSSIP_CONTACT (object),
307                                                  g_value_get_int (value));
308                 break;
309         case PROP_AVATAR:
310                 gossip_contact_set_avatar (GOSSIP_CONTACT (object),
311                                            g_value_get_boxed (value));
312                 break;
313         case PROP_ACCOUNT:
314                 gossip_contact_set_account (GOSSIP_CONTACT (object),
315                                             MC_ACCOUNT (g_value_get_object (value)));
316                 break;
317         case PROP_HANDLE:
318                 gossip_contact_set_handle (GOSSIP_CONTACT (object),
319                                            g_value_get_uint (value));
320                 break;
321         default:
322                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
323                 break;
324         };
325 }
326
327 GossipContact *
328 gossip_contact_new (McAccount *account)
329 {
330         return g_object_new (GOSSIP_TYPE_CONTACT,
331                              "account", account,
332                              NULL);
333 }
334
335 GossipContact *
336 gossip_contact_new_full (McAccount *account,
337                          const gchar   *id,
338                          const gchar   *name)
339 {
340         return g_object_new (GOSSIP_TYPE_CONTACT,
341                              "account", account,
342                              "name", name,
343                              "id", id,
344                              NULL);
345 }
346
347 const gchar *
348 gossip_contact_get_id (GossipContact *contact)
349 {
350         GossipContactPriv *priv;
351
352         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), "");
353
354         priv = GET_PRIV (contact);
355
356         if (priv->id) {
357                 return priv->id;
358         }
359
360         return "";
361 }
362
363 const gchar *
364 gossip_contact_get_name (GossipContact *contact)
365 {
366         GossipContactPriv *priv;
367
368         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), "");
369
370         priv = GET_PRIV (contact);
371
372         if (priv->name == NULL) {
373                 return gossip_contact_get_id (contact);
374         }
375
376         return priv->name;
377 }
378
379 GossipAvatar *
380 gossip_contact_get_avatar (GossipContact *contact)
381 {
382         GossipContactPriv *priv;
383
384         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
385
386         priv = GET_PRIV (contact);
387
388         return priv->avatar;
389 }
390
391 McAccount *
392 gossip_contact_get_account (GossipContact *contact)
393 {
394         GossipContactPriv *priv;
395
396         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
397
398         priv = GET_PRIV (contact);
399
400         return priv->account;
401 }
402
403 GossipPresence *
404 gossip_contact_get_active_presence (GossipContact *contact)
405 {
406         GossipContactPriv *priv;
407
408         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
409
410         priv = GET_PRIV (contact);
411
412         if (priv->presences) {
413                 /* Highest priority of the presences is first */
414                 return GOSSIP_PRESENCE (priv->presences->data);
415         }
416
417         return NULL;
418 }
419
420 GossipPresence *
421 gossip_contact_get_presence_for_resource (GossipContact *contact,
422                                           const gchar   *resource)
423 {
424         GossipContactPriv *priv;
425         GList             *l;
426
427         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
428         g_return_val_if_fail (resource != NULL, NULL);
429
430         priv = GET_PRIV (contact);
431
432         for (l = priv->presences; l; l = l->next) {
433                 const gchar *p_res;
434
435                 p_res = gossip_presence_get_resource (GOSSIP_PRESENCE (l->data));
436                 if (p_res && strcmp (resource, p_res) == 0) {
437                         return GOSSIP_PRESENCE (l->data);
438                 }
439         }
440
441         return NULL;
442 }
443
444 GList *
445 gossip_contact_get_presence_list (GossipContact *contact)
446 {
447         GossipContactPriv *priv;
448
449         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
450
451         priv = GET_PRIV (contact);
452
453         return priv->presences;
454 }
455
456 GList *
457 gossip_contact_get_groups (GossipContact *contact)
458 {
459         GossipContactPriv *priv;
460
461         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
462
463         priv = GET_PRIV (contact);
464
465         return priv->groups;
466 }
467
468 GossipSubscription
469 gossip_contact_get_subscription (GossipContact *contact)
470 {
471         GossipContactPriv *priv;
472
473         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact),
474                               GOSSIP_SUBSCRIPTION_NONE);
475
476         priv = GET_PRIV (contact);
477
478         return priv->subscription;
479 }
480
481 guint
482 gossip_contact_get_handle (GossipContact *contact)
483 {
484         GossipContactPriv *priv;
485
486         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), 0);
487
488         priv = GET_PRIV (contact);
489
490         return priv->handle;
491 }
492
493 void
494 gossip_contact_set_id (GossipContact *contact,
495                        const gchar   *id)
496 {
497         GossipContactPriv *priv;
498
499         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
500         g_return_if_fail (id != NULL);
501
502         priv = GET_PRIV (contact);
503
504         g_free (priv->id);
505         priv->id = g_strdup (id);
506
507         g_object_notify (G_OBJECT (contact), "id");
508 }
509
510 void
511 gossip_contact_set_name (GossipContact *contact,
512                          const gchar   *name)
513 {
514         GossipContactPriv *priv;
515
516         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
517         g_return_if_fail (name != NULL);
518
519         priv = GET_PRIV (contact);
520
521         g_free (priv->name);
522         priv->name = g_strdup (name);
523
524         g_object_notify (G_OBJECT (contact), "name");
525 }
526
527 static void
528 contact_set_presences (GossipContact *contact,
529                        GList         *presences)
530 {
531         GossipContactPriv *priv;
532
533         priv = GET_PRIV (contact);
534
535         if (priv->presences) {
536                 g_list_foreach (priv->presences, (GFunc) g_object_unref, NULL);
537                 g_list_free (priv->presences);
538         }
539
540         priv->presences = g_list_copy (presences);
541         g_list_foreach (priv->presences, (GFunc) g_object_ref, NULL);
542
543         g_object_notify (G_OBJECT (contact), "presences");
544 }
545
546 void
547 gossip_contact_set_avatar (GossipContact *contact,
548                            GossipAvatar  *avatar)
549 {
550         GossipContactPriv *priv;
551
552         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
553
554         priv = GET_PRIV (contact);
555
556         if (priv->avatar) {
557                 gossip_avatar_unref (priv->avatar);
558                 priv->avatar = NULL;
559         }
560
561         if (avatar) {
562                 priv->avatar = gossip_avatar_ref (avatar);
563         }
564
565         g_object_notify (G_OBJECT (contact), "avatar");
566 }
567
568 void
569 gossip_contact_set_account (GossipContact *contact,
570                             McAccount *account)
571 {
572         GossipContactPriv *priv;
573
574         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
575         g_return_if_fail (MC_IS_ACCOUNT (account));
576
577         priv = GET_PRIV (contact);
578
579         if (priv->account) {
580                 g_object_unref (priv->account);
581         }
582
583         if (account) {
584                 priv->account = g_object_ref (account);
585         } else {
586                 priv->account = NULL;
587         }
588
589         g_object_notify (G_OBJECT (contact), "account");
590 }
591
592 void
593 gossip_contact_add_presence (GossipContact  *contact,
594                              GossipPresence *presence)
595 {
596         GossipContactPriv *priv;
597         GossipPresence    *this_presence;
598         GList             *l;
599
600         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
601         g_return_if_fail (GOSSIP_IS_PRESENCE (presence));
602
603         priv = GET_PRIV (contact);
604
605         for (l = priv->presences; l && presence; l = l->next) {
606                 this_presence = l->data;
607
608                 if (gossip_presence_resource_equal (this_presence, presence)) {
609                         gint ref_count;
610
611                         ref_count = G_OBJECT (presence)->ref_count;
612
613                         /* Remove old presence for this resource, we
614                          * would use g_list_remove_all() here but we
615                          * want to make sure we unref for each
616                          * instance we find it in the list.
617                          */
618                         priv->presences = g_list_remove (priv->presences, this_presence);
619                         g_object_unref (this_presence);
620
621                         if (!priv->presences || ref_count <= 1) {
622                                 break;
623                         }
624
625                         /* Reset list to beginnging to make sure we
626                          * didn't miss any duplicates.
627                          */
628                         l = priv->presences;
629                 }
630         }
631
632         /* Add new presence */
633         priv->presences = g_list_insert_sorted (priv->presences,
634                                                 g_object_ref (presence),
635                                                 gossip_presence_sort_func);
636
637         g_object_notify (G_OBJECT (contact), "presences");
638 }
639
640 void
641 gossip_contact_remove_presence (GossipContact  *contact,
642                                 GossipPresence *presence)
643 {
644         GossipContactPriv *priv;
645         GossipPresence    *this_presence;
646         GList             *l;
647
648         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
649         g_return_if_fail (GOSSIP_IS_PRESENCE (presence));
650
651         priv = GET_PRIV (contact);
652
653         for (l = priv->presences; l; l = l->next) {
654                 this_presence = l->data;
655
656                 if (gossip_presence_resource_equal (this_presence, presence)) {
657                         gint ref_count;
658
659                         ref_count = G_OBJECT (presence)->ref_count;
660
661                         /* Remove old presence for this resource, we
662                          * would use g_list_remove_all() here but we
663                          * want to make sure we unref for each
664                          * instance we find it in the list.
665                          */
666                         priv->presences = g_list_remove (priv->presences, this_presence);
667                         g_object_unref (this_presence);
668
669                         if (!priv->presences || ref_count <= 1) {
670                                 break;
671                         }
672
673                         /* Reset list to beginnging to make sure we
674                          * didn't miss any duplicates.
675                          */
676                         l = priv->presences;
677                 }
678         }
679
680         priv->presences = g_list_sort (priv->presences,
681                                        gossip_presence_sort_func);
682
683         g_object_notify (G_OBJECT (contact), "presences");
684 }
685
686 void
687 gossip_contact_set_groups (GossipContact *contact,
688                            GList         *groups)
689 {
690         GossipContactPriv *priv;
691         GList             *old_groups, *l;
692
693         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
694
695         priv = GET_PRIV (contact);
696
697         old_groups = priv->groups;
698         priv->groups = NULL;
699
700         for (l = groups; l; l = l->next) {
701                 priv->groups = g_list_append (priv->groups,
702                                               g_strdup (l->data));
703         }
704
705         g_list_foreach (old_groups, (GFunc) g_free, NULL);
706         g_list_free (old_groups);
707
708         g_object_notify (G_OBJECT (contact), "groups");
709 }
710
711 void
712 gossip_contact_set_subscription (GossipContact      *contact,
713                                  GossipSubscription  subscription)
714 {
715         GossipContactPriv *priv;
716
717         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
718
719         priv = GET_PRIV (contact);
720
721         priv->subscription = subscription;
722
723         g_object_notify (G_OBJECT (contact), "subscription");
724 }
725
726 void
727 gossip_contact_set_handle (GossipContact *contact,
728                            guint          handle)
729 {
730         GossipContactPriv *priv;
731
732         priv = GET_PRIV (contact);
733
734         priv->handle = handle;
735
736         g_object_notify (G_OBJECT (contact), "handle");
737 }
738
739 gboolean
740 gossip_contact_is_online (GossipContact *contact)
741 {
742         GossipContactPriv *priv;
743
744         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), FALSE);
745
746         priv = GET_PRIV (contact);
747
748         if (priv->presences == NULL) {
749                 return FALSE;
750         }
751
752         return TRUE;
753 }
754
755 const gchar *
756 gossip_contact_get_status (GossipContact *contact)
757 {
758         GossipContactPriv *priv;
759
760         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), "");
761
762         priv = GET_PRIV (contact);
763
764         if (priv->presences) {
765                 GossipPresence *p;
766                 const gchar    *status;
767
768                 p = GOSSIP_PRESENCE (priv->presences->data);
769                 status = gossip_presence_get_status (p);
770                 if (!status) {
771                         status = gossip_presence_state_get_default_status (gossip_presence_get_state (p));
772                 }
773                 return status;
774         } else {
775                 return _("Offline");
776         }
777 }
778
779 gboolean
780 gossip_contact_equal (gconstpointer v1,
781                       gconstpointer v2)
782 {
783         McAccount   *account_a;
784         McAccount   *account_b;
785         const gchar *id_a;
786         const gchar *id_b;
787
788         g_return_val_if_fail (GOSSIP_IS_CONTACT (v1), FALSE);
789         g_return_val_if_fail (GOSSIP_IS_CONTACT (v2), FALSE);
790
791         account_a = gossip_contact_get_account (GOSSIP_CONTACT (v1));
792         account_b = gossip_contact_get_account (GOSSIP_CONTACT (v2));
793
794         id_a = gossip_contact_get_id (GOSSIP_CONTACT (v1));
795         id_b = gossip_contact_get_id (GOSSIP_CONTACT (v2));
796
797         return gossip_account_equal (account_a, account_b) && g_str_equal (id_a, id_b);
798 }
799
800 guint
801 gossip_contact_hash (gconstpointer key)
802 {
803         GossipContactPriv *priv;
804         guint              hash;
805
806         g_return_val_if_fail (GOSSIP_IS_CONTACT (key), +1);
807
808         priv = GET_PRIV (GOSSIP_CONTACT (key));
809
810         hash = gossip_account_hash (gossip_contact_get_account (GOSSIP_CONTACT (key)));
811         hash += g_str_hash (gossip_contact_get_id (GOSSIP_CONTACT (key)));
812
813         return hash;
814 }
815