]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-chat.c
Rework EmpathyChat's API, it is now a subclass of GtkBin.
[empathy.git] / libempathy / empathy-tp-chat.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  * 
19  * Authors: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <telepathy-glib/channel.h>
27 #include <telepathy-glib/dbus.h>
28 #include <telepathy-glib/util.h>
29
30 #include "empathy-tp-chat.h"
31 #include "empathy-contact-factory.h"
32 #include "empathy-contact-list.h"
33 #include "empathy-marshal.h"
34 #include "empathy-debug.h"
35 #include "empathy-time.h"
36 #include "empathy-utils.h"
37
38 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
39                        EMPATHY_TYPE_TP_CHAT, EmpathyTpChatPriv))
40
41 #define DEBUG_DOMAIN "TpChat"
42
43 struct _EmpathyTpChatPriv {
44         EmpathyContactFactory *factory;
45         EmpathyContact        *user;
46         EmpathyContact        *remote_contact;
47         EmpathyTpGroup        *group;
48         McAccount             *account;
49         TpChannel             *channel;
50         gchar                 *id;
51         MissionControl        *mc;
52         gboolean               acknowledge;
53         TpChan                *tp_chan;
54         gboolean               had_pending_messages;
55         GSList                *message_queue;
56         gboolean               had_properties_list;
57         GPtrArray             *properties;
58 };
59
60 typedef struct {
61         gchar          *name;
62         guint           id;
63         TpPropertyFlags flags;
64         GValue         *value;
65 } TpChatProperty;
66
67 static void empathy_tp_chat_class_init (EmpathyTpChatClass      *klass);
68 static void empathy_tp_chat_init       (EmpathyTpChat           *chat);
69 static void tp_chat_iface_init         (EmpathyContactListIface *iface);
70
71 enum {
72         PROP_0,
73         PROP_ACCOUNT,
74         PROP_TP_CHAN,
75         PROP_CHANNEL,
76         PROP_ACKNOWLEDGE,
77         PROP_REMOTE_CONTACT,
78 };
79
80 enum {
81         MESSAGE_RECEIVED,
82         SEND_ERROR,
83         CHAT_STATE_CHANGED,
84         PROPERTY_CHANGED,
85         DESTROY,
86         LAST_SIGNAL
87 };
88
89 static guint signals[LAST_SIGNAL];
90
91 G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat, empathy_tp_chat, G_TYPE_OBJECT,
92                          G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
93                                                 tp_chat_iface_init));
94
95 static void
96 tp_chat_invalidated_cb (TpProxy       *proxy,
97                         guint          domain,
98                         gint           code,
99                         gchar         *message,
100                         EmpathyTpChat *chat)
101 {
102         EmpathyTpChatPriv *priv = GET_PRIV (chat);
103
104         empathy_debug (DEBUG_DOMAIN, "Channel invalidated: %s", message);
105
106         g_object_unref (priv->channel);
107         g_object_unref (priv->tp_chan);
108         priv->channel = NULL;
109         priv->tp_chan = NULL;
110
111         g_signal_emit (chat, signals[DESTROY], 0);
112 }
113
114 static void
115 tp_chat_async_cb (TpChannel *proxy,
116                   const GError *error,
117                   gpointer user_data,
118                   GObject *weak_object)
119 {
120         if (error) {
121                 empathy_debug (DEBUG_DOMAIN, "Error %s: %s",
122                                user_data, error->message);
123         }
124 }
125
126 static void
127 tp_chat_member_added_cb (EmpathyTpGroup *group,
128                          EmpathyContact *contact,
129                          EmpathyContact *actor,
130                          guint           reason,
131                          const gchar    *message,
132                          EmpathyTpChat  *chat)
133 {
134         g_signal_emit_by_name (chat, "members-changed",
135                                contact, actor, reason, message,
136                                TRUE);
137 }
138
139 static void
140 tp_chat_member_removed_cb (EmpathyTpGroup *group,
141                            EmpathyContact *contact,
142                            EmpathyContact *actor,
143                            guint           reason,
144                            const gchar    *message,
145                            EmpathyTpChat  *chat)
146 {
147         g_signal_emit_by_name (chat, "members-changed",
148                                contact, actor, reason, message,
149                                FALSE);
150 }
151 static void
152 tp_chat_local_pending_cb  (EmpathyTpGroup *group,
153                            EmpathyContact *contact,
154                            EmpathyContact *actor,
155                            guint           reason,
156                            const gchar    *message,
157                            EmpathyTpChat  *chat)
158 {
159         g_signal_emit_by_name (chat, "pendings-changed",
160                                contact, actor, reason, message,
161                                TRUE);
162 }
163
164 static void
165 tp_chat_add (EmpathyContactList *list,
166              EmpathyContact     *contact,
167              const gchar        *message)
168 {
169         EmpathyTpChatPriv *priv = GET_PRIV (list);
170
171         g_return_if_fail (EMPATHY_IS_TP_CHAT (list));
172         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
173
174         if (priv->group) {
175                 empathy_tp_group_add_member (priv->group, contact, message);
176         }
177 }
178
179 static void
180 tp_chat_remove (EmpathyContactList *list,
181                 EmpathyContact     *contact,
182                 const gchar        *message)
183 {
184         EmpathyTpChatPriv *priv = GET_PRIV (list);
185
186         g_return_if_fail (EMPATHY_IS_TP_CHAT (list));
187         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
188
189         if (priv->group) {
190                 empathy_tp_group_remove_member (priv->group, contact, message);
191         }
192 }
193
194 static GList *
195 tp_chat_get_members (EmpathyContactList *list)
196 {
197         EmpathyTpChatPriv *priv = GET_PRIV (list);
198         GList             *members = NULL;
199
200         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list), NULL);
201
202         if (priv->group) {
203                 members = empathy_tp_group_get_members (priv->group);
204         } else {
205                 members = g_list_prepend (members, g_object_ref (priv->user));
206                 members = g_list_prepend (members, g_object_ref (priv->remote_contact));
207         }
208
209         return members;
210 }
211
212 static EmpathyMessage *
213 tp_chat_build_message (EmpathyTpChat *chat,
214                        guint          type,
215                        guint          timestamp,
216                        guint          from_handle,
217                        const gchar   *message_body)
218 {
219         EmpathyTpChatPriv *priv;
220         EmpathyMessage    *message;
221         EmpathyContact    *sender;
222
223         priv = GET_PRIV (chat);
224
225         if (from_handle == 0) {
226                 sender = g_object_ref (priv->user);
227         } else {
228                 sender = empathy_contact_factory_get_from_handle (priv->factory,
229                                                                   priv->account,
230                                                                   from_handle);
231         }
232
233         message = empathy_message_new (message_body);
234         empathy_message_set_type (message, type);
235         empathy_message_set_sender (message, sender);
236         empathy_message_set_receiver (message, priv->user);
237         empathy_message_set_timestamp (message, timestamp);
238
239         g_object_unref (sender);
240
241         return message;
242 }
243
244 static void
245 tp_chat_sender_ready_notify_cb (EmpathyContact *contact,
246                                 GParamSpec     *param_spec,
247                                 EmpathyTpChat  *chat)
248 {
249         EmpathyTpChatPriv   *priv = GET_PRIV (chat);
250         EmpathyMessage      *message;
251         EmpathyContactReady  ready;
252         EmpathyContact      *sender;
253         gboolean             removed = FALSE;
254
255         /* Emit all messages queued until we find a message with not
256          * ready sender. When leaving this loop, sender is the first not ready
257          * contact queued and removed tells if at least one message got removed
258          * from the queue. */
259         while (priv->message_queue) {
260                 message = priv->message_queue->data;
261                 sender = empathy_message_get_sender (message);
262                 ready = empathy_contact_get_ready (sender);
263
264                 if (!(ready & EMPATHY_CONTACT_READY_NAME)) {
265                         break;
266                 }
267
268                 empathy_debug (DEBUG_DOMAIN, "Queued message ready");
269                 g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
270                 priv->message_queue = g_slist_remove (priv->message_queue,
271                                                       message);
272                 g_object_unref (message);
273                 removed = TRUE;
274         }
275
276         if (removed) {
277                 g_signal_handlers_disconnect_by_func (contact,
278                                                       tp_chat_sender_ready_notify_cb,
279                                                       chat);
280
281                 if (priv->message_queue) {
282                         g_signal_connect (sender, "notify::ready",
283                                           G_CALLBACK (tp_chat_sender_ready_notify_cb),
284                                           chat);
285                 }
286         }
287 }
288
289 static void
290 tp_chat_emit_or_queue_message (EmpathyTpChat  *chat,
291                                EmpathyMessage *message)
292 {
293         EmpathyTpChatPriv   *priv = GET_PRIV (chat);
294         EmpathyContact      *sender;
295         EmpathyContactReady  ready;
296
297         if (priv->message_queue != NULL) {
298                 empathy_debug (DEBUG_DOMAIN, "Message queue not empty");
299                 priv->message_queue = g_slist_append (priv->message_queue,
300                                                       g_object_ref (message));
301                 return;
302         }
303
304         sender = empathy_message_get_sender (message);
305         ready = empathy_contact_get_ready (sender);
306         if (ready & EMPATHY_CONTACT_READY_NAME) {
307                 empathy_debug (DEBUG_DOMAIN, "Message queue empty and sender ready");
308                 g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
309                 return;
310         }
311
312         empathy_debug (DEBUG_DOMAIN, "Sender not ready");
313         priv->message_queue = g_slist_append (priv->message_queue, 
314                                               g_object_ref (message));
315         g_signal_connect (sender, "notify::ready",
316                           G_CALLBACK (tp_chat_sender_ready_notify_cb),
317                           chat);
318 }
319
320 static void
321 tp_chat_received_cb (TpChannel   *channel,
322                      guint        message_id,
323                      guint        timestamp,
324                      guint        from_handle,
325                      guint        message_type,
326                      guint        message_flags,
327                      const gchar *message_body,
328                      gpointer     user_data,
329                      GObject     *chat)
330 {
331         EmpathyTpChatPriv *priv = GET_PRIV (chat);
332         EmpathyMessage    *message;
333
334         if (!priv->had_pending_messages) {
335                 return;
336         }
337  
338         empathy_debug (DEBUG_DOMAIN, "Message received: %s", message_body);
339
340         message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
341                                          message_type,
342                                          timestamp,
343                                          from_handle,
344                                          message_body);
345
346         tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message);
347         g_object_unref (message);
348
349         if (priv->acknowledge) {
350                 GArray *message_ids;
351
352                 message_ids = g_array_new (FALSE, FALSE, sizeof (guint));
353                 g_array_append_val (message_ids, message_id);
354                 tp_cli_channel_type_text_call_acknowledge_pending_messages (priv->channel,
355                                                                             -1,
356                                                                             message_ids,
357                                                                             tp_chat_async_cb,
358                                                                             "acknowledging pending messages",
359                                                                             NULL,
360                                                                             chat);
361                 g_array_free (message_ids, TRUE);
362         }
363 }
364
365 static void
366 tp_chat_sent_cb (TpChannel   *channel,
367                  guint        timestamp,
368                  guint        message_type,
369                  const gchar *message_body,
370                  gpointer     user_data,
371                  GObject     *chat)
372 {
373         EmpathyMessage *message;
374
375         empathy_debug (DEBUG_DOMAIN, "Message sent: %s", message_body);
376
377         message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
378                                          message_type,
379                                          timestamp,
380                                          0,
381                                          message_body);
382
383         tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message);
384         g_object_unref (message);
385 }
386
387 static void
388 tp_chat_send_error_cb (TpChannel   *channel,
389                        guint        error_code,
390                        guint        timestamp,
391                        guint        message_type,
392                        const gchar *message_body,
393                        gpointer     user_data,
394                        GObject     *chat)
395 {
396         EmpathyMessage *message;
397
398         empathy_debug (DEBUG_DOMAIN, "Message sent error: %s (%d)",
399                        message_body, error_code);
400
401         message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
402                                          message_type,
403                                          timestamp,
404                                          0,
405                                          message_body);
406
407         g_signal_emit (chat, signals[SEND_ERROR], 0, message, error_code);
408         g_object_unref (message);
409 }
410
411 static void
412 tp_chat_state_changed_cb (TpChannel *channel,
413                           guint      handle,
414                           guint      state,
415                           gpointer   user_data,
416                           GObject   *chat)
417 {
418         EmpathyTpChatPriv *priv = GET_PRIV (chat);
419         EmpathyContact    *contact;
420
421         contact = empathy_contact_factory_get_from_handle (priv->factory,
422                                                            priv->account,
423                                                            handle);
424
425         empathy_debug (DEBUG_DOMAIN, "Chat state changed for %s (%d): %d",
426                       empathy_contact_get_name (contact),
427                       handle, state);
428
429         g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
430         g_object_unref (contact);
431 }
432
433 static void
434 tp_chat_list_pending_messages_cb (TpChannel       *channel,
435                                   const GPtrArray *messages_list,
436                                   const GError    *error,
437                                   gpointer         user_data,
438                                   GObject         *chat)
439 {
440         EmpathyTpChatPriv *priv = GET_PRIV (chat);
441         guint              i;
442
443         priv->had_pending_messages = TRUE;
444
445         if (error) {
446                 empathy_debug (DEBUG_DOMAIN, "Error listing pending messages: %s",
447                                error->message);
448                 return;
449         }
450
451         for (i = 0; i < messages_list->len; i++) {
452                 EmpathyMessage *message;
453                 GValueArray    *message_struct;
454                 const gchar    *message_body;
455                 guint           message_id;
456                 guint           timestamp;
457                 guint           from_handle;
458                 guint           message_type;
459                 guint           message_flags;
460
461                 message_struct = g_ptr_array_index (messages_list, i);
462
463                 message_id = g_value_get_uint (g_value_array_get_nth (message_struct, 0));
464                 timestamp = g_value_get_uint (g_value_array_get_nth (message_struct, 1));
465                 from_handle = g_value_get_uint (g_value_array_get_nth (message_struct, 2));
466                 message_type = g_value_get_uint (g_value_array_get_nth (message_struct, 3));
467                 message_flags = g_value_get_uint (g_value_array_get_nth (message_struct, 4));
468                 message_body = g_value_get_string (g_value_array_get_nth (message_struct, 5));
469
470                 empathy_debug (DEBUG_DOMAIN, "Message pending: %s", message_body);
471
472                 message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
473                                                  message_type,
474                                                  timestamp,
475                                                  from_handle,
476                                                  message_body);
477
478                 tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message);
479                 g_object_unref (message);
480         }
481 }
482
483 static void
484 tp_chat_property_flags_changed_cb (TpProxy         *proxy,
485                                    const GPtrArray *properties,
486                                    gpointer         user_data,
487                                    GObject         *chat)
488 {
489         EmpathyTpChatPriv *priv = GET_PRIV (chat);
490         guint              i, j;
491
492         if (!priv->had_properties_list || !properties) {
493                 return;
494         }
495
496         for (i = 0; i < properties->len; i++) {
497                 GValueArray    *prop_struct;
498                 TpChatProperty *property;
499                 guint           id;
500                 guint           flags;
501
502                 prop_struct = g_ptr_array_index (properties, i);
503                 id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
504                 flags = g_value_get_uint (g_value_array_get_nth (prop_struct, 1));
505
506                 for (j = 0; j < priv->properties->len; j++) {
507                         property = g_ptr_array_index (priv->properties, j);
508                         if (property->id == id) {
509                                 property->flags = flags;
510                                 empathy_debug (DEBUG_DOMAIN,
511                                                "property %s flags changed: %d",
512                                                property->name, property->flags);
513                                 break;
514                         }
515                 }
516         }
517 }
518
519 static void
520 tp_chat_properties_changed_cb (TpProxy         *proxy,
521                                const GPtrArray *properties,
522                                gpointer         user_data,
523                                GObject         *chat)
524 {
525         EmpathyTpChatPriv *priv = GET_PRIV (chat);
526         guint              i, j;
527
528         if (!priv->had_properties_list || !properties) {
529                 return;
530         }
531
532         for (i = 0; i < properties->len; i++) {
533                 GValueArray    *prop_struct;
534                 TpChatProperty *property;
535                 guint           id;
536                 GValue         *src_value;
537
538                 prop_struct = g_ptr_array_index (properties, i);
539                 id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
540                 src_value = g_value_get_boxed (g_value_array_get_nth (prop_struct, 1));
541
542                 for (j = 0; j < priv->properties->len; j++) {
543                         property = g_ptr_array_index (priv->properties, j);
544                         if (property->id == id) {
545                                 if (property->value) {
546                                         g_value_copy (src_value, property->value);
547                                 } else {
548                                         property->value = tp_g_value_slice_dup (src_value);
549                                 }
550
551                                 empathy_debug (DEBUG_DOMAIN, "property %s changed",
552                                                property->name);
553                                 g_signal_emit (chat, signals[PROPERTY_CHANGED], 0,
554                                                property->name, property->value);
555                                 break;
556                         }
557                 }
558         }
559 }
560
561 static void
562 tp_chat_get_properties_cb (TpProxy         *proxy,
563                            const GPtrArray *properties,
564                            const GError    *error,
565                            gpointer         user_data,
566                            GObject         *chat)
567 {
568         if (error) {
569                 empathy_debug (DEBUG_DOMAIN, "Error getting properties: %s",
570                                error->message);
571                 return;
572         }
573
574         tp_chat_properties_changed_cb (proxy, properties, user_data, chat);
575 }
576
577 static void
578 tp_chat_list_properties_cb (TpProxy         *proxy,
579                             const GPtrArray *properties,
580                             const GError    *error,
581                             gpointer         user_data,
582                             GObject         *chat)
583 {
584         EmpathyTpChatPriv *priv = GET_PRIV (chat);
585         GArray            *ids;
586         guint              i;
587
588         priv->had_properties_list = TRUE;
589
590         if (error) {
591                 empathy_debug (DEBUG_DOMAIN, "Error listing properties: %s",
592                                error->message);
593                 return;
594         }
595
596         ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), properties->len);
597         priv->properties = g_ptr_array_sized_new (properties->len);
598         for (i = 0; i < properties->len; i++) {
599                 GValueArray    *prop_struct;
600                 TpChatProperty *property;
601
602                 prop_struct = g_ptr_array_index (properties, i);
603                 property = g_slice_new0 (TpChatProperty);
604                 property->id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
605                 property->name = g_value_dup_string (g_value_array_get_nth (prop_struct, 1));
606                 property->flags = g_value_get_uint (g_value_array_get_nth (prop_struct, 3));
607
608                 empathy_debug (DEBUG_DOMAIN, "Adding property name=%s id=%d flags=%d",
609                                property->name, property->id, property->flags);
610                 g_ptr_array_add (priv->properties, property);
611                 if (property->flags & TP_PROPERTY_FLAG_READ) {
612                         g_array_append_val (ids, property->id);
613                 }
614         }
615
616         tp_cli_properties_interface_call_get_properties (proxy, -1,
617                                                          ids,
618                                                          tp_chat_get_properties_cb,
619                                                          NULL, NULL,
620                                                          chat);
621
622         g_array_free (ids, TRUE);
623 }
624
625 void
626 empathy_tp_chat_set_property (EmpathyTpChat *chat,
627                               const gchar   *name,
628                               const GValue  *value)
629 {
630         EmpathyTpChatPriv *priv = GET_PRIV (chat);
631         TpChatProperty    *property;
632         guint              i;
633
634         for (i = 0; i < priv->properties->len; i++) {
635                 property = g_ptr_array_index (priv->properties, i);
636                 if (!tp_strdiff (property->name, name)) {
637                         GPtrArray   *properties;
638                         GValueArray *prop;
639                         GValue       id = {0, };
640                         GValue       dest_value = {0, };
641
642                         if (!(property->flags & TP_PROPERTY_FLAG_WRITE)) {
643                                 break;
644                         }
645
646                         g_value_init (&id, G_TYPE_UINT);
647                         g_value_init (&dest_value, G_TYPE_VALUE);
648                         g_value_set_uint (&id, property->id);
649                         g_value_set_boxed (&dest_value, value);
650
651                         prop = g_value_array_new (2);
652                         g_value_array_append (prop, &id);
653                         g_value_array_append (prop, &dest_value);
654
655                         properties = g_ptr_array_sized_new (1);
656                         g_ptr_array_add (properties, prop);
657
658                         empathy_debug (DEBUG_DOMAIN, "Set property %s", name);
659                         tp_cli_properties_interface_call_set_properties (priv->channel, -1,
660                                                                          properties,
661                                                                          (tp_cli_properties_interface_callback_for_set_properties)
662                                                                          tp_chat_async_cb,
663                                                                          "Seting property", NULL,
664                                                                          G_OBJECT (chat));
665
666                         g_ptr_array_free (properties, TRUE);
667                         g_value_array_free (prop);
668
669                         break;
670                 }
671         }
672 }
673
674 static void
675 tp_chat_channel_ready_cb (EmpathyTpChat *chat)
676 {
677         EmpathyTpChatPriv *priv = GET_PRIV (chat);
678
679         empathy_debug (DEBUG_DOMAIN, "Channel ready");
680
681         if (tp_proxy_has_interface_by_id (priv->channel,
682                                           TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP)) {
683                 priv->group = empathy_tp_group_new (priv->account, priv->tp_chan);
684
685                 g_signal_connect (priv->group, "member-added",
686                                   G_CALLBACK (tp_chat_member_added_cb),
687                                   chat);
688                 g_signal_connect (priv->group, "member-removed",
689                                   G_CALLBACK (tp_chat_member_removed_cb),
690                                   chat);
691                 g_signal_connect (priv->group, "local-pending",
692                                   G_CALLBACK (tp_chat_local_pending_cb),
693                                   chat);
694         } else {
695                 priv->remote_contact = empathy_contact_factory_get_from_handle (priv->factory,
696                                                                                 priv->account,
697                                                                                 priv->tp_chan->handle);
698                 g_object_notify (G_OBJECT (chat), "remote-contact");
699         }
700         
701         if (tp_proxy_has_interface_by_id (priv->channel,
702                                           TP_IFACE_QUARK_PROPERTIES_INTERFACE)) {
703                 tp_cli_properties_interface_call_list_properties (priv->channel, -1,
704                                                                   tp_chat_list_properties_cb,
705                                                                   NULL, NULL,
706                                                                   G_OBJECT (chat));
707                 tp_cli_properties_interface_connect_to_properties_changed (priv->channel,
708                                                                            tp_chat_properties_changed_cb,
709                                                                            NULL, NULL,
710                                                                            G_OBJECT (chat), NULL);
711                 tp_cli_properties_interface_connect_to_property_flags_changed (priv->channel,
712                                                                                tp_chat_property_flags_changed_cb,
713                                                                                NULL, NULL,
714                                                                                G_OBJECT (chat), NULL);
715         }
716
717         tp_cli_channel_type_text_call_list_pending_messages (priv->channel, -1,
718                                                              priv->acknowledge,
719                                                              tp_chat_list_pending_messages_cb,
720                                                              NULL, NULL,
721                                                              G_OBJECT (chat));
722
723         tp_cli_channel_type_text_connect_to_received (priv->channel,
724                                                       tp_chat_received_cb,
725                                                       NULL, NULL,
726                                                       G_OBJECT (chat), NULL);
727         tp_cli_channel_type_text_connect_to_sent (priv->channel,
728                                                   tp_chat_sent_cb,
729                                                   NULL, NULL,
730                                                   G_OBJECT (chat), NULL);
731         tp_cli_channel_type_text_connect_to_send_error (priv->channel,
732                                                         tp_chat_send_error_cb,
733                                                         NULL, NULL,
734                                                         G_OBJECT (chat), NULL);
735         tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv->channel,
736                                                                            tp_chat_state_changed_cb,
737                                                                            NULL, NULL,
738                                                                            G_OBJECT (chat), NULL);
739         tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv->channel,
740                                                                            tp_chat_state_changed_cb,
741                                                                            NULL, NULL,
742                                                                            G_OBJECT (chat), NULL);
743 }
744
745 static void
746 tp_chat_finalize (GObject *object)
747 {
748         EmpathyTpChatPriv *priv = GET_PRIV (object);
749         guint              i;
750
751         if (priv->acknowledge && priv->channel) {
752                 empathy_debug (DEBUG_DOMAIN, "Closing channel...");
753                 tp_cli_channel_call_close (priv->channel, -1,
754                                            tp_chat_async_cb,
755                                            "closing channel", NULL,
756                                            NULL);
757         }
758
759         if (priv->channel) {
760                 g_signal_handlers_disconnect_by_func (priv->channel,
761                                                       tp_chat_invalidated_cb,
762                                                       object);
763                 g_object_unref (priv->channel);
764         }
765         if (priv->tp_chan) {
766                 g_object_unref (priv->tp_chan);
767         }
768
769         if (priv->properties) {
770                 for (i = 0; i < priv->properties->len; i++) {
771                         TpChatProperty *property;
772
773                         property = g_ptr_array_index (priv->properties, i);
774                         g_free (property->name);
775                         if (property->value) {
776                                 tp_g_value_slice_free (property->value);
777                         }
778                         g_slice_free (TpChatProperty, property);
779                 }
780                 g_ptr_array_free (priv->properties, TRUE);
781         }
782
783         if (priv->remote_contact) {
784                 g_object_unref (priv->remote_contact);
785         }
786         if (priv->group) {
787                 g_object_unref (priv->group);
788         }
789
790         g_object_unref (priv->factory);
791         g_object_unref (priv->user);
792         g_object_unref (priv->account);
793         g_object_unref (priv->mc);
794         g_free (priv->id);
795
796         G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
797 }
798
799 static GObject *
800 tp_chat_constructor (GType                  type,
801                      guint                  n_props,
802                      GObjectConstructParam *props)
803 {
804         GObject           *chat;
805         EmpathyTpChatPriv *priv;
806         gboolean           channel_ready;
807
808         chat = G_OBJECT_CLASS (empathy_tp_chat_parent_class)->constructor (type, n_props, props);
809
810         priv = GET_PRIV (chat);
811         priv->factory = empathy_contact_factory_new ();
812         priv->user = empathy_contact_factory_get_user (priv->factory, priv->account);
813         priv->mc = empathy_mission_control_new ();
814
815         g_signal_connect (priv->channel, "invalidated",
816                           G_CALLBACK (tp_chat_invalidated_cb),
817                           chat);
818
819         g_object_get (priv->channel, "channel-ready", &channel_ready, NULL);
820         if (channel_ready) {
821                 tp_chat_channel_ready_cb (EMPATHY_TP_CHAT (chat));
822         } else {
823                 g_signal_connect_swapped (priv->channel, "notify::channel-ready",
824                                           G_CALLBACK (tp_chat_channel_ready_cb),
825                                           chat);
826         }
827
828         return chat;
829 }
830
831 static void
832 tp_chat_get_property (GObject    *object,
833                       guint       param_id,
834                       GValue     *value,
835                       GParamSpec *pspec)
836 {
837         EmpathyTpChatPriv *priv = GET_PRIV (object);
838
839         switch (param_id) {
840         case PROP_ACCOUNT:
841                 g_value_set_object (value, priv->account);
842                 break;
843         case PROP_TP_CHAN:
844                 g_value_set_object (value, priv->tp_chan);
845                 break;
846         case PROP_CHANNEL:
847                 g_value_set_object (value, priv->channel);
848                 break;
849         case PROP_ACKNOWLEDGE:
850                 g_value_set_boolean (value, priv->acknowledge);
851                 break;
852         case PROP_REMOTE_CONTACT:
853                 g_value_set_object (value, priv->remote_contact);
854                 break;
855         default:
856                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
857                 break;
858         };
859 }
860
861 static void
862 tp_chat_set_property (GObject      *object,
863                       guint         param_id,
864                       const GValue *value,
865                       GParamSpec   *pspec)
866 {
867         EmpathyTpChatPriv *priv = GET_PRIV (object);
868
869         switch (param_id) {
870         case PROP_ACCOUNT:
871                 priv->account = g_object_ref (g_value_get_object (value));
872                 break;
873         case PROP_TP_CHAN:
874                 priv->tp_chan = g_object_ref (g_value_get_object (value));
875                 break;
876         case PROP_CHANNEL:
877                 priv->channel = g_object_ref (g_value_get_object (value));
878                 break;
879         case PROP_ACKNOWLEDGE:
880                 priv->acknowledge = g_value_get_boolean (value);
881                 break;
882         default:
883                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
884                 break;
885         };
886 }
887
888 static void
889 empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
890 {
891         GObjectClass *object_class = G_OBJECT_CLASS (klass);
892
893         object_class->finalize = tp_chat_finalize;
894         object_class->constructor = tp_chat_constructor;
895         object_class->get_property = tp_chat_get_property;
896         object_class->set_property = tp_chat_set_property;
897
898         /* Construct properties */
899         g_object_class_install_property (object_class,
900                                          PROP_ACCOUNT,
901                                          g_param_spec_object ("account",
902                                                               "channel Account",
903                                                               "The account associated with the channel",
904                                                               MC_TYPE_ACCOUNT,
905                                                               G_PARAM_READWRITE |
906                                                               G_PARAM_CONSTRUCT_ONLY));
907         g_object_class_install_property (object_class,
908                                          PROP_TP_CHAN,
909                                          g_param_spec_object ("tp-chan",
910                                                               "telepathy channel",
911                                                               "The text channel for the chat",
912                                                               TELEPATHY_CHAN_TYPE,
913                                                               G_PARAM_READWRITE |
914                                                               G_PARAM_CONSTRUCT_ONLY));
915         g_object_class_install_property (object_class,
916                                          PROP_CHANNEL,
917                                          g_param_spec_object ("channel",
918                                                               "telepathy channel",
919                                                               "The text channel for the chat",
920                                                               TP_TYPE_CHANNEL,
921                                                               G_PARAM_READWRITE |
922                                                               G_PARAM_CONSTRUCT_ONLY));
923         g_object_class_install_property (object_class,
924                                          PROP_ACKNOWLEDGE,
925                                          g_param_spec_boolean ("acknowledge",
926                                                                "acknowledge messages",
927                                                                "Wheter or not received messages should be acknowledged",
928                                                                FALSE,
929                                                                G_PARAM_READWRITE |
930                                                                G_PARAM_CONSTRUCT));
931         g_object_class_install_property (object_class,
932                                          PROP_REMOTE_CONTACT,
933                                          g_param_spec_object ("remote-contact",
934                                                               "The remote contact",
935                                                               "The remote contact if there is no group iface on the channel",
936                                                               EMPATHY_TYPE_CONTACT,
937                                                               G_PARAM_READABLE));
938
939         /* Signals */
940         signals[MESSAGE_RECEIVED] =
941                 g_signal_new ("message-received",
942                               G_TYPE_FROM_CLASS (klass),
943                               G_SIGNAL_RUN_LAST,
944                               0,
945                               NULL, NULL,
946                               g_cclosure_marshal_VOID__OBJECT,
947                               G_TYPE_NONE,
948                               1, EMPATHY_TYPE_MESSAGE);
949
950         signals[SEND_ERROR] =
951                 g_signal_new ("send-error",
952                               G_TYPE_FROM_CLASS (klass),
953                               G_SIGNAL_RUN_LAST,
954                               0,
955                               NULL, NULL,
956                               _empathy_marshal_VOID__OBJECT_UINT,
957                               G_TYPE_NONE,
958                               2, EMPATHY_TYPE_MESSAGE, G_TYPE_UINT);
959
960         signals[CHAT_STATE_CHANGED] =
961                 g_signal_new ("chat-state-changed",
962                               G_TYPE_FROM_CLASS (klass),
963                               G_SIGNAL_RUN_LAST,
964                               0,
965                               NULL, NULL,
966                               _empathy_marshal_VOID__OBJECT_UINT,
967                               G_TYPE_NONE,
968                               2, EMPATHY_TYPE_CONTACT, G_TYPE_UINT);
969
970         signals[PROPERTY_CHANGED] =
971                 g_signal_new ("property-changed",
972                               G_TYPE_FROM_CLASS (klass),
973                               G_SIGNAL_RUN_LAST,
974                               0,
975                               NULL, NULL,
976                               _empathy_marshal_VOID__STRING_BOXED,
977                               G_TYPE_NONE,
978                               2, G_TYPE_STRING, G_TYPE_VALUE);
979
980         signals[DESTROY] =
981                 g_signal_new ("destroy",
982                               G_TYPE_FROM_CLASS (klass),
983                               G_SIGNAL_RUN_LAST,
984                               0,
985                               NULL, NULL,
986                               g_cclosure_marshal_VOID__VOID,
987                               G_TYPE_NONE,
988                               0);
989
990         g_type_class_add_private (object_class, sizeof (EmpathyTpChatPriv));
991 }
992
993 static void
994 empathy_tp_chat_init (EmpathyTpChat *chat)
995 {
996 }
997
998 static void
999 tp_chat_iface_init (EmpathyContactListIface *iface)
1000 {
1001         iface->add         = tp_chat_add;
1002         iface->remove      = tp_chat_remove;
1003         iface->get_members = tp_chat_get_members;
1004 }
1005
1006 EmpathyTpChat *
1007 empathy_tp_chat_new (McAccount *account,
1008                      TpChan    *tp_chan,
1009                      gboolean   acknowledge)
1010 {
1011         EmpathyTpChat  *chat;
1012         TpChannel      *channel;
1013         TpConnection   *connection;
1014         MissionControl *mc;
1015         TpConn         *tp_conn;
1016
1017         mc = empathy_mission_control_new ();
1018         tp_conn = mission_control_get_connection (mc, account, NULL);
1019         connection = tp_conn_dup_connection (tp_conn);
1020         channel = tp_chan_dup_channel (tp_chan, connection, NULL);
1021
1022         chat = g_object_new (EMPATHY_TYPE_TP_CHAT, 
1023                              "account", account,
1024                              "channel", channel,
1025                              "tp-chan", tp_chan,
1026                              "acknowledge", acknowledge,
1027                              NULL);
1028
1029         g_object_unref (channel);
1030         g_object_unref (tp_conn);
1031         g_object_unref (connection);
1032         g_object_unref (mc);
1033
1034         return chat;
1035 }
1036
1037 EmpathyTpChat *
1038 empathy_tp_chat_new_with_contact (EmpathyContact *contact)
1039 {
1040         EmpathyTpChat  *chat;
1041         MissionControl *mc;
1042         McAccount      *account;
1043         TpConn         *tp_conn;
1044         TpChan         *text_chan;
1045         const gchar    *bus_name;
1046         guint           handle;
1047
1048         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
1049
1050         mc = empathy_mission_control_new ();
1051         account = empathy_contact_get_account (contact);
1052
1053         if (mission_control_get_connection_status (mc, account, NULL) != 0) {
1054                 /* The account is not connected. */
1055                 return NULL;
1056         }
1057
1058         tp_conn = mission_control_get_connection (mc, account, NULL);
1059         g_return_val_if_fail (tp_conn != NULL, NULL);
1060         bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn));
1061         handle = empathy_contact_get_handle (contact);
1062
1063         text_chan = tp_conn_new_channel (tp_get_bus (),
1064                                          tp_conn,
1065                                          bus_name,
1066                                          TP_IFACE_CHANNEL_TYPE_TEXT,
1067                                          TP_HANDLE_TYPE_CONTACT,
1068                                          handle,
1069                                          TRUE);
1070
1071         chat = empathy_tp_chat_new (account, text_chan, TRUE);
1072
1073         g_object_unref (tp_conn);
1074         g_object_unref (text_chan);
1075         g_object_unref (mc);
1076
1077         return chat;
1078 }
1079
1080 TpChan *
1081 empathy_tp_chat_get_channel (EmpathyTpChat *chat)
1082 {
1083         EmpathyTpChatPriv *priv;
1084
1085         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
1086
1087         priv = GET_PRIV (chat);
1088
1089         return priv->tp_chan;
1090 }
1091
1092 McAccount *
1093 empathy_tp_chat_get_account (EmpathyTpChat *chat)
1094 {
1095         EmpathyTpChatPriv *priv;
1096
1097         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
1098
1099         priv = GET_PRIV (chat);
1100
1101         return priv->account;
1102 }
1103
1104 void
1105 empathy_tp_chat_send (EmpathyTpChat *chat,
1106                       EmpathyMessage *message)
1107 {
1108         EmpathyTpChatPriv  *priv;
1109         const gchar        *message_body;
1110         EmpathyMessageType  message_type;
1111
1112         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
1113         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
1114
1115         priv = GET_PRIV (chat);
1116
1117         message_body = empathy_message_get_body (message);
1118         message_type = empathy_message_get_type (message);
1119
1120         empathy_debug (DEBUG_DOMAIN, "Sending message: %s", message_body);
1121         tp_cli_channel_type_text_call_send (priv->channel, -1,
1122                                             message_type,
1123                                             message_body,
1124                                             tp_chat_async_cb,
1125                                             "sending message", NULL,
1126                                             G_OBJECT (chat));
1127 }
1128
1129 void
1130 empathy_tp_chat_set_state (EmpathyTpChat      *chat,
1131                            TpChannelChatState  state)
1132 {
1133         EmpathyTpChatPriv *priv;
1134
1135         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
1136
1137         priv = GET_PRIV (chat);
1138
1139         empathy_debug (DEBUG_DOMAIN, "Set state: %d", state);
1140         tp_cli_channel_interface_chat_state_call_set_chat_state (priv->channel, -1,
1141                                                                  state,
1142                                                                  tp_chat_async_cb,
1143                                                                  "setting chat state",
1144                                                                  NULL,
1145                                                                  G_OBJECT (chat));
1146 }
1147
1148 const gchar *
1149 empathy_tp_chat_get_id (EmpathyTpChat *chat)
1150 {
1151         EmpathyTpChatPriv *priv;
1152
1153         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
1154
1155         priv = GET_PRIV (chat);
1156
1157         if (!priv->id) {
1158                 priv->id = empathy_inspect_channel (priv->account, priv->tp_chan);
1159         }
1160
1161         return priv->id;
1162 }
1163
1164 EmpathyContact *
1165 empathy_tp_chat_get_remote_contact (EmpathyTpChat *chat)
1166 {
1167         EmpathyTpChatPriv *priv = GET_PRIV (chat);
1168
1169         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
1170
1171         return priv->remote_contact;
1172 }
1173