]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-group.c
f5cbb70815dfde87fe3e472cab44ff6b5516d63d
[empathy.git] / libempathy / empathy-tp-group.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2006 Xavier Claessens <xclaesse@gmail.com>
4  * Copyright (C) 2007-2008 Collabora Ltd.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * Authors: Xavier Claessens <xclaesse@gmail.com>
21  */
22
23 #include <config.h>
24
25 #include <libmissioncontrol/mc-account.h>
26
27 #include <telepathy-glib/channel.h>
28 #include <telepathy-glib/util.h>
29 #include <telepathy-glib/interfaces.h>
30
31 #include "empathy-tp-group.h"
32 #include "empathy-contact-factory.h"
33 #include "empathy-debug.h"
34 #include "empathy-utils.h"
35 #include "empathy-marshal.h"
36
37 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
38                        EMPATHY_TYPE_TP_GROUP, EmpathyTpGroupPriv))
39
40 #define DEBUG_DOMAIN "TpGroup"
41
42 struct _EmpathyTpGroupPriv {
43         TpChannel             *channel;
44         gboolean               ready;
45
46         EmpathyContactFactory *factory;
47         McAccount             *account;
48         gchar                 *group_name;
49         guint                  self_handle;
50         GList                 *members;
51         GList                 *local_pendings;
52         GList                 *remote_pendings;
53 };
54
55 static void empathy_tp_group_class_init (EmpathyTpGroupClass *klass);
56 static void empathy_tp_group_init       (EmpathyTpGroup      *group);
57
58 enum {
59         MEMBER_ADDED,
60         MEMBER_REMOVED,
61         LOCAL_PENDING,
62         REMOTE_PENDING,
63         DESTROY,
64         LAST_SIGNAL
65 };
66
67 enum {
68         PROP_0,
69         PROP_CHANNEL,
70         PROP_READY
71 };
72
73 static guint signals[LAST_SIGNAL];
74
75 G_DEFINE_TYPE (EmpathyTpGroup, empathy_tp_group, G_TYPE_OBJECT);
76
77 static EmpathyContact *
78 tp_group_get_contact (EmpathyTpGroup *group,
79                       guint           handle)
80 {
81         EmpathyTpGroupPriv *priv = GET_PRIV (group);
82         EmpathyContact     *contact = NULL;
83         
84         if (handle != 0) {
85                 contact = empathy_contact_factory_get_from_handle (priv->factory,
86                                                                    priv->account,
87                                                                    handle);
88         }
89
90         if (contact && handle == priv->self_handle) {
91                 empathy_contact_set_is_user (contact, TRUE);
92         }
93
94         return contact;
95 }
96
97 static GList *
98 tp_group_get_contacts (EmpathyTpGroup *group,
99                        const GArray   *handles)
100 {
101         EmpathyTpGroupPriv *priv = GET_PRIV (group);
102         GList              *contacts,  *l;
103
104         if (!handles) {
105                 return NULL;
106         }
107
108         contacts = empathy_contact_factory_get_from_handles (priv->factory,
109                                                              priv->account,
110                                                              handles);
111
112         /* FIXME: Only useful if the group has a different self handle than
113          * the connection, otherwise the contact factory already set that
114          * property. That can be known using group flags. */
115         for (l = contacts; l; l = l->next) {
116                 if (empathy_contact_get_handle (l->data) == priv->self_handle) {
117                         empathy_contact_set_is_user (l->data, TRUE);
118                 }
119         }
120
121         return contacts;
122 }
123
124 EmpathyPendingInfo *
125 empathy_pending_info_new (EmpathyContact *member,
126                           EmpathyContact *actor,
127                           const gchar    *message)
128 {
129         EmpathyPendingInfo *info;
130
131         info = g_slice_new0 (EmpathyPendingInfo);
132
133         if (member) {
134                 info->member = g_object_ref (member);
135         }
136         if (actor) {
137                 info->actor = g_object_ref (actor);
138         }
139         if (message) {
140                 info->message = g_strdup (message);
141         }
142
143         return info;
144 }
145
146 void
147 empathy_pending_info_free (EmpathyPendingInfo *info)
148 {
149         if (!info) {
150                 return;
151         }
152
153         if (info->member) {
154                 g_object_unref (info->member);
155         }
156         if (info->actor) {
157                 g_object_unref (info->actor);
158         }
159         g_free (info->message);
160
161         g_slice_free (EmpathyPendingInfo, info);
162 }
163
164 static gint
165 tp_group_local_pending_find (gconstpointer a,
166                              gconstpointer b)
167 {
168         const EmpathyPendingInfo *info = a;
169
170         return (info->member != b);
171 }
172
173 static void
174 tp_group_remove_from_pendings (EmpathyTpGroup *group,
175                                EmpathyContact *contact)
176 {
177         EmpathyTpGroupPriv *priv = GET_PRIV (group);
178         GList              *l;
179
180         /* local pending */
181         l = g_list_find_custom (priv->local_pendings,
182                                 contact,
183                                 tp_group_local_pending_find);
184         if (l) {
185                 empathy_pending_info_free (l->data);
186                 priv->local_pendings = g_list_delete_link (priv->local_pendings, l);
187         }
188
189         /* remote pending */
190         l = g_list_find (priv->remote_pendings, contact);
191         if (l) {
192                 g_object_unref (l->data);
193                 priv->remote_pendings = g_list_delete_link (priv->remote_pendings, l);
194         }
195 }
196
197 static void
198 tp_group_update_members (EmpathyTpGroup *group,
199                          const gchar    *message,
200                          const GArray   *added,
201                          const GArray   *removed,
202                          const GArray   *local_pending,
203                          const GArray   *remote_pending,
204                          guint           actor,
205                          guint           reason)
206 {
207         EmpathyTpGroupPriv *priv = GET_PRIV (group);
208         EmpathyContact     *actor_contact = NULL;
209         GList              *contacts, *l, *ll;
210
211         actor_contact = tp_group_get_contact (group, actor);
212
213         empathy_debug (DEBUG_DOMAIN, "Members changed for list %s:\n"
214                                      "  added-len=%d, current-len=%d\n"
215                                      "  removed-len=%d\n"
216                                      "  local-pending-len=%d, current-len=%d\n"
217                                      "  remote-pending-len=%d, current-len=%d",
218                        priv->group_name,
219                        added ? added->len : 0, g_list_length (priv->members),
220                        removed ? removed->len : 0,
221                        local_pending ? local_pending->len : 0,
222                        g_list_length (priv->local_pendings),
223                        remote_pending ? remote_pending->len : 0,
224                        g_list_length (priv->remote_pendings));
225
226         /* Contacts added */
227         contacts = tp_group_get_contacts (group, added);
228         for (l = contacts; l; l = l->next) {
229                 tp_group_remove_from_pendings (group, l->data);
230
231                 /* If the contact is not yet member, add it and emit signal */
232                 if (!g_list_find (priv->members, l->data)) {
233                         priv->members = g_list_prepend (priv->members,
234                                                         g_object_ref (l->data));
235                         g_signal_emit (group, signals[MEMBER_ADDED], 0,
236                                        l->data, actor_contact, reason, message);
237                 }
238                 g_object_unref (l->data);
239         }
240         g_list_free (contacts);
241
242         /* Contacts removed */
243         contacts = tp_group_get_contacts (group, removed);
244         for (l = contacts; l; l = l->next) {
245                 tp_group_remove_from_pendings (group, l->data);
246
247                 /* If the contact is member, remove it and emit signal */
248                 if ((ll = g_list_find (priv->members, l->data))) {
249                         g_object_unref (ll->data);
250                         priv->members = g_list_delete_link (priv->members, ll);
251                         g_signal_emit (group, signals[MEMBER_REMOVED], 0,
252                                        l->data, actor_contact, reason, message);
253                 }
254                 g_object_unref (l->data);
255         }
256         g_list_free (contacts);
257
258         /* Contacts local pending */
259         contacts = tp_group_get_contacts (group, local_pending);
260         for (l = contacts; l; l = l->next) {
261                 /* If the contact is not yet local-pending, add it and emit signal */
262                 if (!g_list_find_custom (priv->local_pendings, l->data,
263                                          tp_group_local_pending_find)) {
264                         EmpathyPendingInfo *info;
265
266                         info = empathy_pending_info_new (l->data,
267                                                          actor_contact,
268                                                          message);
269
270                         priv->local_pendings = g_list_prepend (priv->local_pendings, info);
271                         g_signal_emit (group, signals[LOCAL_PENDING], 0,
272                                        l->data, actor_contact, reason, message);
273                 }
274                 g_object_unref (l->data);
275         }
276         g_list_free (contacts);
277
278         /* Contacts remote pending */
279         contacts = tp_group_get_contacts (group, remote_pending);
280         for (l = contacts; l; l = l->next) {
281                 /* If the contact is not yet remote-pending, add it and emit signal */
282                 if (!g_list_find (priv->remote_pendings, l->data)) {
283                         priv->remote_pendings = g_list_prepend (priv->remote_pendings,
284                                                                 g_object_ref (l->data));
285                         g_signal_emit (group, signals[REMOTE_PENDING], 0,
286                                        l->data, actor_contact, reason, message);
287                 }
288                 g_object_unref (l->data);
289         }
290         g_list_free (contacts);
291
292         if (actor_contact) {
293                 g_object_unref (actor_contact);
294         }
295
296         empathy_debug (DEBUG_DOMAIN, "Members changed done for list %s:\n"
297                                      "  members-len=%d\n"
298                                      "  local-pendings-len=%d\n"
299                                      "  remote-pendings-len=%d",
300                        priv->group_name,
301                        g_list_length (priv->members),
302                        g_list_length (priv->local_pendings),
303                        g_list_length (priv->remote_pendings));
304 }
305
306 static void
307 tp_group_members_changed_cb (TpChannel    *channel,
308                              const gchar  *message,
309                              const GArray *added,
310                              const GArray *removed,
311                              const GArray *local_pending,
312                              const GArray *remote_pending,
313                              guint         actor,
314                              guint         reason,
315                              gpointer      user_data,
316                              GObject      *group)
317 {
318         EmpathyTpGroupPriv *priv = GET_PRIV (group);
319
320         if (priv->ready) {
321                 tp_group_update_members (EMPATHY_TP_GROUP (group), message,
322                                          added, removed,
323                                          local_pending, remote_pending,
324                                          actor, reason);
325         }
326 }
327
328 static void
329 tp_group_get_members_cb (TpChannel    *channel,
330                          const GArray *handles,
331                          const GError *error,
332                          gpointer      user_data,
333                          GObject      *group)
334 {
335         EmpathyTpGroupPriv *priv = GET_PRIV (group);
336
337         if (error) {
338                 empathy_debug (DEBUG_DOMAIN, "Failed to get members: %s",
339                                error->message);
340                 return;
341         }
342
343         tp_group_update_members (EMPATHY_TP_GROUP (group),
344                                  NULL,    /* message */
345                                  handles, /* added */
346                                  NULL,    /* removed */
347                                  NULL,    /* local_pending */
348                                  NULL,    /* remote_pending */
349                                  0,       /* actor */
350                                  0);      /* reason */
351
352         empathy_debug (DEBUG_DOMAIN, "Ready");
353         priv->ready = TRUE;
354         g_object_notify (group, "ready");
355 }
356
357 static void
358 tp_group_get_local_pending_cb (TpChannel        *channel,
359                                const GPtrArray  *array,
360                                const GError     *error,
361                                gpointer          user_data,
362                                GObject          *group)
363 {
364         GArray *handles;
365         guint   i = 0;
366         
367         if (error) {
368                 empathy_debug (DEBUG_DOMAIN, "Failed to get local pendings: %s",
369                                error->message);
370                 return;
371         }
372
373         handles = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
374         g_array_append_val (handles, i);
375         for (i = 0; array->len > i; i++) {
376                 GValueArray *pending_struct;
377                 const gchar *message;
378                 guint        member_handle;
379                 guint        actor_handle;
380                 guint        reason;
381
382                 pending_struct = g_ptr_array_index (array, i);
383                 member_handle = g_value_get_uint (g_value_array_get_nth (pending_struct, 0));
384                 actor_handle = g_value_get_uint (g_value_array_get_nth (pending_struct, 1));
385                 reason = g_value_get_uint (g_value_array_get_nth (pending_struct, 2));
386                 message = g_value_get_string (g_value_array_get_nth (pending_struct, 3));
387
388                 g_array_index (handles, guint, 0) = member_handle;
389
390                 tp_group_update_members (EMPATHY_TP_GROUP (group),
391                                          message,      /* message */
392                                          NULL,         /* added */
393                                          NULL,         /* removed */
394                                          handles,      /* local_pending */
395                                          NULL,         /* remote_pending */
396                                          actor_handle, /* actor */
397                                          reason);      /* reason */
398         }
399         g_array_free (handles, TRUE);
400 }
401
402 static void
403 tp_group_get_remote_pending_cb (TpChannel    *channel,
404                                 const GArray *handles,
405                                 const GError *error,
406                                 gpointer      user_data,
407                                 GObject      *group)
408 {
409         if (error) {
410                 empathy_debug (DEBUG_DOMAIN, "Failed to get remote pendings: %s",
411                                error->message);
412                 return;
413         }
414
415         tp_group_update_members (EMPATHY_TP_GROUP (group),
416                                  NULL,    /* message */
417                                  NULL,    /* added */
418                                  NULL,    /* removed */
419                                  NULL,    /* local_pending */
420                                  handles, /* remote_pending */
421                                  0,       /* actor */
422                                  0);      /* reason */
423 }
424
425 static void
426 tp_group_inspect_handles_cb (TpConnection  *connection,
427                              const gchar  **names,
428                              const GError  *error,
429                              gpointer       user_data,
430                              GObject       *group)
431 {
432         EmpathyTpGroupPriv *priv = GET_PRIV (group);
433
434         if (error) {
435                 empathy_debug (DEBUG_DOMAIN, "Failed to inspect channel handle: %s",
436                                error->message);
437                 return;
438         }
439
440         priv->group_name = g_strdup (*names);
441 }
442
443 static void
444 tp_group_invalidated_cb (TpProxy        *proxy,
445                          guint           domain,
446                          gint            code,
447                          gchar          *message,
448                          EmpathyTpGroup *group)
449 {
450         empathy_debug (DEBUG_DOMAIN, "Channel invalidated: %s", message);
451         g_signal_emit (group, signals[DESTROY], 0);
452 }
453
454 static void
455 tp_group_get_self_handle_cb (TpChannel    *proxy,
456                              guint         handle,
457                              const GError *error,
458                              gpointer      user_data,
459                              GObject      *group)
460 {
461         EmpathyTpGroupPriv *priv = GET_PRIV (group);
462         TpConnection       *connection;
463         guint               channel_handle;
464         guint               channel_handle_type;
465         GArray             *handles;
466
467         if (error) {
468                 empathy_debug (DEBUG_DOMAIN, "Failed to get self handle: %s",
469                                error->message);
470                 return;
471         }
472
473         priv->self_handle = handle;
474         tp_cli_channel_interface_group_connect_to_members_changed (priv->channel,
475                                                                    tp_group_members_changed_cb,
476                                                                    NULL, NULL,
477                                                                    group, NULL);
478
479         /* GetMembers is called last, so it will be the last to get the reply,
480          * so we'll be ready once that call return. */
481         g_object_get (priv->channel,
482                       "connection", &connection,
483                       "handle-type", &channel_handle_type,
484                       "handle", &channel_handle,
485                       NULL);
486         handles = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
487         g_array_prepend_val (handles, channel_handle);
488         tp_cli_connection_call_inspect_handles (connection, -1,
489                                                 channel_handle_type,
490                                                 handles,
491                                                 tp_group_inspect_handles_cb,
492                                                 NULL, NULL,
493                                                 group);
494         g_array_free (handles, TRUE);
495
496         tp_cli_channel_interface_group_call_get_local_pending_members_with_info
497                                                         (priv->channel, -1,
498                                                          tp_group_get_local_pending_cb,
499                                                          NULL, NULL, 
500                                                          group);
501         tp_cli_channel_interface_group_call_get_remote_pending_members
502                                                         (priv->channel, -1,
503                                                          tp_group_get_remote_pending_cb,
504                                                          NULL, NULL, 
505                                                          group);
506         tp_cli_channel_interface_group_call_get_members (priv->channel, -1,
507                                                          tp_group_get_members_cb,
508                                                          NULL, NULL, 
509                                                          group);
510 }
511
512 static void
513 tp_group_factory_ready_cb (EmpathyTpGroup *group)
514 {
515         EmpathyTpGroupPriv      *priv = GET_PRIV (group);
516         EmpathyTpContactFactory *tp_factory;
517
518         tp_factory = empathy_contact_factory_get_tp_factory (priv->factory, priv->account);
519         g_signal_handlers_disconnect_by_func (tp_factory, tp_group_factory_ready_cb, group);
520         tp_cli_channel_interface_group_call_get_self_handle (priv->channel, -1,
521                                                              tp_group_get_self_handle_cb,
522                                                              NULL, NULL,
523                                                              G_OBJECT (group));
524 }
525
526 static void
527 tp_group_channel_ready_cb (EmpathyTpGroup *group)
528 {
529         EmpathyTpGroupPriv      *priv = GET_PRIV (group);
530         EmpathyTpContactFactory *tp_factory;
531
532         tp_factory = empathy_contact_factory_get_tp_factory (priv->factory,
533                                                              priv->account);
534         if (empathy_tp_contact_factory_is_ready (tp_factory)) {
535                 tp_group_factory_ready_cb (group);
536         } else {
537                 g_signal_connect_swapped (tp_factory, "notify::ready",
538                                           G_CALLBACK (tp_group_factory_ready_cb),
539                                           group);
540         }
541 }
542
543 static void
544 tp_group_finalize (GObject *object)
545 {
546         EmpathyTpGroupPriv      *priv = GET_PRIV (object);
547         EmpathyTpContactFactory *tp_factory;
548
549         empathy_debug (DEBUG_DOMAIN, "finalize: %p", object);
550
551         tp_factory = empathy_contact_factory_get_tp_factory (priv->factory, priv->account);
552         g_signal_handlers_disconnect_by_func (tp_factory, tp_group_factory_ready_cb, object);
553
554         if (priv->channel) {
555                 g_signal_handlers_disconnect_by_func (priv->channel,
556                                                       tp_group_invalidated_cb,
557                                                       object);
558                 g_object_unref (priv->channel);
559         }
560         if (priv->account) {
561                 g_object_unref (priv->account);
562         }
563         if (priv->factory) {
564                 g_object_unref (priv->factory);
565         }
566         g_free (priv->group_name);
567
568         g_list_foreach (priv->members, (GFunc) g_object_unref, NULL);
569         g_list_free (priv->members);
570
571         g_list_foreach (priv->local_pendings, (GFunc) empathy_pending_info_free, NULL);
572         g_list_free (priv->local_pendings);
573
574         g_list_foreach (priv->remote_pendings, (GFunc) g_object_unref, NULL);
575         g_list_free (priv->remote_pendings);
576
577         G_OBJECT_CLASS (empathy_tp_group_parent_class)->finalize (object);
578 }
579
580 static void
581 tp_group_constructed (GObject *group)
582 {
583         EmpathyTpGroupPriv *priv = GET_PRIV (group);
584         gboolean            channel_ready;
585
586         priv->factory = empathy_contact_factory_new ();
587         priv->account = empathy_channel_get_account (priv->channel);
588
589         g_signal_connect (priv->channel, "invalidated",
590                           G_CALLBACK (tp_group_invalidated_cb),
591                           group);
592
593         g_object_get (priv->channel, "channel-ready", &channel_ready, NULL);
594         if (channel_ready) {
595                 tp_group_channel_ready_cb (EMPATHY_TP_GROUP (group));
596         } else {
597                 g_signal_connect_swapped (priv->channel, "notify::channel-ready",
598                                           G_CALLBACK (tp_group_channel_ready_cb),
599                                           group);
600         }
601 }
602
603 static void
604 tp_group_get_property (GObject    *object,
605                        guint       param_id,
606                        GValue     *value,
607                        GParamSpec *pspec)
608 {
609         EmpathyTpGroupPriv *priv = GET_PRIV (object);
610
611         switch (param_id) {
612         case PROP_CHANNEL:
613                 g_value_set_object (value, priv->channel);
614                 break;
615         case PROP_READY:
616                 g_value_set_boolean (value, priv->ready);
617                 break;
618         default:
619                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
620                 break;
621         };
622 }
623
624 static void
625 tp_group_set_property (GObject      *object,
626                        guint         param_id,
627                        const GValue *value,
628                        GParamSpec   *pspec)
629 {
630         EmpathyTpGroupPriv *priv = GET_PRIV (object);
631
632         switch (param_id) {
633         case PROP_CHANNEL:
634                 priv->channel = g_object_ref (g_value_get_object (value));
635                 break;
636         default:
637                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
638                 break;
639         };
640 }
641
642 static void
643 empathy_tp_group_class_init (EmpathyTpGroupClass *klass)
644 {
645         GObjectClass *object_class = G_OBJECT_CLASS (klass);
646
647         object_class->finalize = tp_group_finalize;
648         object_class->constructed = tp_group_constructed;
649         object_class->get_property = tp_group_get_property;
650         object_class->set_property = tp_group_set_property;
651
652         g_object_class_install_property (object_class,
653                                          PROP_CHANNEL,
654                                          g_param_spec_object ("channel",
655                                                               "telepathy channel",
656                                                               "The channel for the group",
657                                                               TP_TYPE_CHANNEL,
658                                                               G_PARAM_READWRITE |
659                                                               G_PARAM_CONSTRUCT_ONLY));
660         g_object_class_install_property (object_class,
661                                          PROP_READY,
662                                          g_param_spec_boolean ("ready",
663                                                                "Is the object ready",
664                                                                "This object can't be used until this becomes true",
665                                                                FALSE,
666                                                                G_PARAM_READABLE));
667
668         signals[MEMBER_ADDED] =
669                 g_signal_new ("member-added",
670                               G_TYPE_FROM_CLASS (klass),
671                               G_SIGNAL_RUN_LAST,
672                               0,
673                               NULL, NULL,
674                               _empathy_marshal_VOID__OBJECT_OBJECT_UINT_STRING,
675                               G_TYPE_NONE,
676                               4, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT, G_TYPE_UINT, G_TYPE_STRING);
677
678         signals[MEMBER_REMOVED] =
679                 g_signal_new ("member-removed",
680                               G_TYPE_FROM_CLASS (klass),
681                               G_SIGNAL_RUN_LAST,
682                               0,
683                               NULL, NULL,
684                               _empathy_marshal_VOID__OBJECT_OBJECT_UINT_STRING,
685                               G_TYPE_NONE,
686                               4, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT, G_TYPE_UINT, G_TYPE_STRING);
687
688         signals[LOCAL_PENDING] =
689                 g_signal_new ("local-pending",
690                               G_TYPE_FROM_CLASS (klass),
691                               G_SIGNAL_RUN_LAST,
692                               0,
693                               NULL, NULL,
694                               _empathy_marshal_VOID__OBJECT_OBJECT_UINT_STRING,
695                               G_TYPE_NONE,
696                               4, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT, G_TYPE_UINT, G_TYPE_STRING);
697
698         signals[REMOTE_PENDING] =
699                 g_signal_new ("remote-pending",
700                               G_TYPE_FROM_CLASS (klass),
701                               G_SIGNAL_RUN_LAST,
702                               0,
703                               NULL, NULL,
704                               _empathy_marshal_VOID__OBJECT_OBJECT_UINT_STRING,
705                               G_TYPE_NONE,
706                               4, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT, G_TYPE_UINT, G_TYPE_STRING);
707
708         signals[DESTROY] =
709                 g_signal_new ("destroy",
710                               G_TYPE_FROM_CLASS (klass),
711                               G_SIGNAL_RUN_LAST,
712                               0,
713                               NULL, NULL,
714                               g_cclosure_marshal_VOID__VOID,
715                               G_TYPE_NONE,
716                               0);
717
718         g_type_class_add_private (object_class, sizeof (EmpathyTpGroupPriv));
719 }
720
721 static void
722 empathy_tp_group_init (EmpathyTpGroup *group)
723 {
724 }
725
726 EmpathyTpGroup *
727 empathy_tp_group_new (TpChannel *channel)
728 {
729         g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
730
731         return g_object_new (EMPATHY_TYPE_TP_GROUP, 
732                              "channel", channel,
733                              NULL);
734 }
735
736 static void
737 tp_group_async_cb (TpChannel    *channel,
738                    const GError *error,
739                    gpointer      user_data,
740                    GObject      *weak_object)
741 {
742         const gchar *msg = user_data;
743
744         if (error) {
745                 empathy_debug (DEBUG_DOMAIN, "%s: %s", msg, error->message);
746         }
747 }
748
749 void
750 empathy_tp_group_close (EmpathyTpGroup *group)
751 {
752         EmpathyTpGroupPriv *priv = GET_PRIV (group);
753
754         g_return_if_fail (EMPATHY_IS_TP_GROUP (group));
755         g_return_if_fail (priv->ready);
756
757         tp_cli_channel_call_close (priv->channel, -1,
758                                    tp_group_async_cb,
759                                    "Failed to close", NULL,
760                                    G_OBJECT (group));
761 }
762
763 static GArray *
764 tp_group_get_handles (GList *contacts)
765 {
766         GArray *handles;
767         guint   length;
768         GList  *l;
769
770         length = g_list_length (contacts);
771         handles = g_array_sized_new (FALSE, FALSE, sizeof (guint), length);
772
773         for (l = contacts; l; l = l->next) {
774                 guint handle;
775
776                 handle = empathy_contact_get_handle (l->data);
777                 g_array_append_val (handles, handle);
778         }
779
780         return handles;
781 }
782
783 void
784 empathy_tp_group_add_members (EmpathyTpGroup *group,
785                               GList          *contacts,
786                               const gchar    *message)
787 {
788         EmpathyTpGroupPriv *priv = GET_PRIV (group);
789         GArray             *handles;
790
791         g_return_if_fail (EMPATHY_IS_TP_GROUP (group));
792         g_return_if_fail (contacts != NULL);
793         g_return_if_fail (priv->ready);
794
795         handles = tp_group_get_handles (contacts);
796         tp_cli_channel_interface_group_call_add_members (priv->channel, -1,
797                                                          handles,
798                                                          message,
799                                                          tp_group_async_cb,
800                                                          "Failed to add members", NULL,
801                                                          G_OBJECT (group));
802         g_array_free (handles, TRUE);
803 }
804
805 void
806 empathy_tp_group_remove_members (EmpathyTpGroup *group,
807                                  GList          *contacts,
808                                  const gchar    *message)
809 {
810         EmpathyTpGroupPriv *priv = GET_PRIV (group);
811         GArray             *handles;
812
813         g_return_if_fail (EMPATHY_IS_TP_GROUP (group));
814         g_return_if_fail (contacts != NULL);
815         g_return_if_fail (priv->ready);
816
817         handles = tp_group_get_handles (contacts);
818         tp_cli_channel_interface_group_call_remove_members (priv->channel, -1,
819                                                             handles,
820                                                             message,
821                                                             tp_group_async_cb,
822                                                             "Failed to remove members", NULL,
823                                                             G_OBJECT (group));
824         g_array_free (handles, TRUE);
825 }
826
827 void
828 empathy_tp_group_add_member (EmpathyTpGroup *group,
829                              EmpathyContact *contact,
830                              const gchar    *message)
831 {
832         GList *contacts;
833
834         contacts = g_list_prepend (NULL, contact);
835         empathy_tp_group_add_members (group, contacts, message);
836         g_list_free (contacts);
837 }
838
839 void
840 empathy_tp_group_remove_member (EmpathyTpGroup *group,
841                                 EmpathyContact *contact,
842                                 const gchar    *message)
843 {
844         GList *contacts;
845
846         contacts = g_list_prepend (NULL, contact);
847         empathy_tp_group_remove_members (group, contacts, message);
848         g_list_free (contacts);
849 }
850
851 GList *
852 empathy_tp_group_get_members (EmpathyTpGroup *group)
853 {
854         EmpathyTpGroupPriv *priv = GET_PRIV (group);
855
856         g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), NULL);
857
858         g_list_foreach (priv->members, (GFunc) g_object_ref, NULL);
859
860         return g_list_copy (priv->members);
861 }
862
863 GList *
864 empathy_tp_group_get_local_pendings (EmpathyTpGroup *group)
865 {
866         EmpathyTpGroupPriv *priv = GET_PRIV (group);
867         GList              *pendings = NULL, *l;
868
869         g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), NULL);
870
871         for (l = priv->local_pendings; l; l = l->next) {
872                 EmpathyPendingInfo *info;
873                 EmpathyPendingInfo *new_info;
874
875                 info = l->data;
876                 new_info = empathy_pending_info_new (info->member,
877                                                      info->actor,
878                                                      info->message);
879                 pendings = g_list_prepend (pendings, new_info);
880         }
881
882         return pendings;
883 }
884
885 GList *
886 empathy_tp_group_get_remote_pendings (EmpathyTpGroup *group)
887 {
888         EmpathyTpGroupPriv *priv = GET_PRIV (group);
889
890         g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), NULL);
891
892         g_list_foreach (priv->remote_pendings, (GFunc) g_object_ref, NULL);
893
894         return g_list_copy (priv->remote_pendings);
895 }
896
897 const gchar *
898 empathy_tp_group_get_name (EmpathyTpGroup *group)
899 {
900         EmpathyTpGroupPriv *priv = GET_PRIV (group);
901
902         g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), NULL);
903         g_return_val_if_fail (priv->ready, NULL);
904
905         return priv->group_name;
906 }
907
908 EmpathyContact *
909 empathy_tp_group_get_self_contact (EmpathyTpGroup *group)
910 {
911         EmpathyTpGroupPriv *priv = GET_PRIV (group);
912
913         g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), NULL);
914         g_return_val_if_fail (priv->ready, NULL);
915
916         return tp_group_get_contact (group, priv->self_handle);
917 }
918
919 gboolean
920 empathy_tp_group_is_member (EmpathyTpGroup *group,
921                             EmpathyContact *contact)
922 {
923         EmpathyTpGroupPriv *priv = GET_PRIV (group);
924
925         g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), FALSE);
926         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
927
928         return g_list_find (priv->members, contact) != NULL;
929 }
930
931 gboolean
932 empathy_tp_group_is_ready (EmpathyTpGroup *group)
933 {
934         EmpathyTpGroupPriv *priv = GET_PRIV (group);
935
936         g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), FALSE);
937
938         return priv->ready;
939 }
940
941 EmpathyPendingInfo *
942 empathy_tp_group_get_invitation (EmpathyTpGroup  *group,
943                                  EmpathyContact **remote_contact)
944 {
945         EmpathyTpGroupPriv *priv = GET_PRIV (group);
946         EmpathyContact     *contact = NULL;
947         EmpathyPendingInfo *invitation = NULL;
948         GList              *l;
949
950         g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), FALSE);
951         g_return_val_if_fail (priv->ready, NULL);
952
953         for (l = priv->local_pendings; l; l = l->next) {
954                 EmpathyPendingInfo *info = l->data;
955
956                 if (empathy_contact_is_user (info->member)) {
957                         invitation = info;
958                         break;
959                 }
960         }
961
962         if (invitation && priv->members && !priv->members->next) {
963                 contact = priv->members->data;
964         }
965         if (!invitation && priv->remote_pendings && !priv->remote_pendings->next) {
966                 contact = priv->remote_pendings->data;
967         }
968
969         if (remote_contact && contact) {
970                 *remote_contact = g_object_ref (contact);
971         }
972
973         return invitation;
974 }
975