]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-chat.c
Prepare a new API for chat properties
[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
29 #include "empathy-tp-chat.h"
30 #include "empathy-contact-factory.h"
31 #include "empathy-marshal.h"
32 #include "empathy-debug.h"
33 #include "empathy-time.h"
34 #include "empathy-utils.h"
35
36 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
37                        EMPATHY_TYPE_TP_CHAT, EmpathyTpChatPriv))
38
39 #define DEBUG_DOMAIN "TpChat"
40
41 struct _EmpathyTpChatPriv {
42         EmpathyContactFactory *factory;
43         EmpathyContact        *user;
44         McAccount             *account;
45         TpChannel             *channel;
46         gchar                 *id;
47         MissionControl        *mc;
48         gboolean               acknowledge;
49         TpChan                *tp_chan;
50         gboolean               had_pending_messages;
51         GSList                *message_queue;
52 };
53
54 static void empathy_tp_chat_class_init (EmpathyTpChatClass *klass);
55 static void empathy_tp_chat_init       (EmpathyTpChat      *chat);
56
57 enum {
58         PROP_0,
59         PROP_ACCOUNT,
60         PROP_CHANNEL,
61         PROP_ACKNOWLEDGE,
62 };
63
64 enum {
65         MESSAGE_RECEIVED,
66         SEND_ERROR,
67         CHAT_STATE_CHANGED,
68         DESTROY,
69         LAST_SIGNAL
70 };
71
72 static guint signals[LAST_SIGNAL];
73
74 G_DEFINE_TYPE (EmpathyTpChat, empathy_tp_chat, G_TYPE_OBJECT);
75
76 static void
77 tp_chat_invalidated_cb (TpProxy       *proxy,
78                         guint          domain,
79                         gint           code,
80                         gchar         *message,
81                         EmpathyTpChat *chat)
82 {
83         EmpathyTpChatPriv *priv = GET_PRIV (chat);
84
85         empathy_debug (DEBUG_DOMAIN, "Channel invalidated: %s", message);
86
87         g_object_unref (priv->channel);
88         g_object_unref (priv->tp_chan);
89         priv->channel = NULL;
90         priv->tp_chan = NULL;
91
92         g_signal_emit (chat, signals[DESTROY], 0);
93 }
94
95 static void
96 tp_chat_async_cb (TpChannel *proxy,
97                   const GError *error,
98                   gpointer user_data,
99                   GObject *weak_object)
100 {
101         if (error) {
102                 empathy_debug (DEBUG_DOMAIN, "Error %s: %s",
103                                user_data, error->message);
104         }
105 }
106
107 static EmpathyMessage *
108 tp_chat_build_message (EmpathyTpChat *chat,
109                        guint          type,
110                        guint          timestamp,
111                        guint          from_handle,
112                        const gchar   *message_body)
113 {
114         EmpathyTpChatPriv *priv;
115         EmpathyMessage    *message;
116         EmpathyContact    *sender;
117
118         priv = GET_PRIV (chat);
119
120         if (from_handle == 0) {
121                 sender = g_object_ref (priv->user);
122         } else {
123                 sender = empathy_contact_factory_get_from_handle (priv->factory,
124                                                                   priv->account,
125                                                                   from_handle);
126         }
127
128         message = empathy_message_new (message_body);
129         empathy_message_set_type (message, type);
130         empathy_message_set_sender (message, sender);
131         empathy_message_set_receiver (message, priv->user);
132         empathy_message_set_timestamp (message, timestamp);
133
134         g_object_unref (sender);
135
136         return message;
137 }
138
139 static void
140 tp_chat_sender_ready_notify_cb (EmpathyContact *contact,
141                                 GParamSpec     *param_spec,
142                                 EmpathyTpChat  *chat)
143 {
144         EmpathyTpChatPriv   *priv = GET_PRIV (chat);
145         EmpathyMessage      *message;
146         EmpathyContactReady  ready;
147         EmpathyContact      *sender;
148         gboolean             removed = FALSE;
149
150         /* Emit all messages queued until we find a message with not
151          * ready sender. When leaving this loop, sender is the first not ready
152          * contact queued and removed tells if at least one message got removed
153          * from the queue. */
154         while (priv->message_queue) {
155                 message = priv->message_queue->data;
156                 sender = empathy_message_get_sender (message);
157                 ready = empathy_contact_get_ready (sender);
158
159                 if (!(ready & EMPATHY_CONTACT_READY_NAME)) {
160                         break;
161                 }
162
163                 empathy_debug (DEBUG_DOMAIN, "Queued message ready");
164                 g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
165                 priv->message_queue = g_slist_remove (priv->message_queue,
166                                                       message);
167                 g_object_unref (message);
168                 removed = TRUE;
169         }
170
171         if (removed) {
172                 g_signal_handlers_disconnect_by_func (contact,
173                                                       tp_chat_sender_ready_notify_cb,
174                                                       chat);
175
176                 if (priv->message_queue) {
177                         g_signal_connect (sender, "notify::ready",
178                                           G_CALLBACK (tp_chat_sender_ready_notify_cb),
179                                           chat);
180                 }
181         }
182 }
183
184 static void
185 tp_chat_emit_or_queue_message (EmpathyTpChat  *chat,
186                                EmpathyMessage *message)
187 {
188         EmpathyTpChatPriv   *priv = GET_PRIV (chat);
189         EmpathyContact      *sender;
190         EmpathyContactReady  ready;
191
192         if (priv->message_queue != NULL) {
193                 empathy_debug (DEBUG_DOMAIN, "Message queue not empty");
194                 priv->message_queue = g_slist_append (priv->message_queue,
195                                                       g_object_ref (message));
196                 return;
197         }
198
199         sender = empathy_message_get_sender (message);
200         ready = empathy_contact_get_ready (sender);
201         if (ready & EMPATHY_CONTACT_READY_NAME) {
202                 empathy_debug (DEBUG_DOMAIN, "Message queue empty and sender ready");
203                 g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
204                 return;
205         }
206
207         empathy_debug (DEBUG_DOMAIN, "Sender not ready");
208         priv->message_queue = g_slist_append (priv->message_queue, 
209                                               g_object_ref (message));
210         g_signal_connect (sender, "notify::ready",
211                           G_CALLBACK (tp_chat_sender_ready_notify_cb),
212                           chat);
213 }
214
215 static void
216 tp_chat_received_cb (TpChannel   *channel,
217                      guint        message_id,
218                      guint        timestamp,
219                      guint        from_handle,
220                      guint        message_type,
221                      guint        message_flags,
222                      const gchar *message_body,
223                      gpointer     user_data,
224                      GObject     *chat)
225 {
226         EmpathyTpChatPriv *priv = GET_PRIV (chat);
227         EmpathyMessage    *message;
228
229         if (!priv->had_pending_messages) {
230                 return;
231         }
232  
233         empathy_debug (DEBUG_DOMAIN, "Message received: %s", message_body);
234
235         message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
236                                          message_type,
237                                          timestamp,
238                                          from_handle,
239                                          message_body);
240
241         tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message);
242         g_object_unref (message);
243
244         if (priv->acknowledge) {
245                 GArray *message_ids;
246
247                 message_ids = g_array_new (FALSE, FALSE, sizeof (guint));
248                 g_array_append_val (message_ids, message_id);
249                 tp_cli_channel_type_text_call_acknowledge_pending_messages (priv->channel,
250                                                                             -1,
251                                                                             message_ids,
252                                                                             tp_chat_async_cb,
253                                                                             "acknowledging pending messages",
254                                                                             NULL,
255                                                                             chat);
256                 g_array_free (message_ids, TRUE);
257         }
258 }
259
260 static void
261 tp_chat_sent_cb (TpChannel   *channel,
262                  guint        timestamp,
263                  guint        message_type,
264                  const gchar *message_body,
265                  gpointer     user_data,
266                  GObject     *chat)
267 {
268         EmpathyMessage *message;
269
270         empathy_debug (DEBUG_DOMAIN, "Message sent: %s", message_body);
271
272         message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
273                                          message_type,
274                                          timestamp,
275                                          0,
276                                          message_body);
277
278         tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message);
279         g_object_unref (message);
280 }
281
282 static void
283 tp_chat_send_error_cb (TpChannel   *channel,
284                        guint        error_code,
285                        guint        timestamp,
286                        guint        message_type,
287                        const gchar *message_body,
288                        gpointer     user_data,
289                        GObject     *chat)
290 {
291         EmpathyMessage *message;
292
293         empathy_debug (DEBUG_DOMAIN, "Message sent error: %s (%d)",
294                        message_body, error_code);
295
296         message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
297                                          message_type,
298                                          timestamp,
299                                          0,
300                                          message_body);
301
302         g_signal_emit (chat, signals[SEND_ERROR], 0, message, error_code);
303         g_object_unref (message);
304 }
305
306 static void
307 tp_chat_state_changed_cb (TpChannel *channel,
308                           guint      handle,
309                           guint      state,
310                           gpointer   user_data,
311                           GObject   *chat)
312 {
313         EmpathyTpChatPriv *priv = GET_PRIV (chat);
314         EmpathyContact    *contact;
315
316         contact = empathy_contact_factory_get_from_handle (priv->factory,
317                                                            priv->account,
318                                                            handle);
319
320         empathy_debug (DEBUG_DOMAIN, "Chat state changed for %s (%d): %d",
321                       empathy_contact_get_name (contact),
322                       handle, state);
323
324         g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
325         g_object_unref (contact);
326 }
327
328 static void
329 tp_chat_list_pending_messages_cb (TpChannel       *channel,
330                                   const GPtrArray *messages_list,
331                                   const GError    *error,
332                                   gpointer         user_data,
333                                   GObject         *chat)
334 {
335         EmpathyTpChatPriv *priv = GET_PRIV (chat);
336         guint              i;
337
338         priv->had_pending_messages = TRUE;
339
340         for (i = 0; i < messages_list->len; i++) {
341                 EmpathyMessage *message;
342                 GValueArray    *message_struct;
343                 const gchar    *message_body;
344                 guint           message_id;
345                 guint           timestamp;
346                 guint           from_handle;
347                 guint           message_type;
348                 guint           message_flags;
349
350                 message_struct = g_ptr_array_index (messages_list, i);
351
352                 message_id = g_value_get_uint (g_value_array_get_nth (message_struct, 0));
353                 timestamp = g_value_get_uint (g_value_array_get_nth (message_struct, 1));
354                 from_handle = g_value_get_uint (g_value_array_get_nth (message_struct, 2));
355                 message_type = g_value_get_uint (g_value_array_get_nth (message_struct, 3));
356                 message_flags = g_value_get_uint (g_value_array_get_nth (message_struct, 4));
357                 message_body = g_value_get_string (g_value_array_get_nth (message_struct, 5));
358
359                 empathy_debug (DEBUG_DOMAIN, "Message pending: %s", message_body);
360
361                 message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
362                                                  message_type,
363                                                  timestamp,
364                                                  from_handle,
365                                                  message_body);
366
367                 tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message);
368                 g_object_unref (message);
369         }
370 }
371
372 static void
373 tp_chat_properties_changed_cb (TpProxy         *proxy,
374                                const GPtrArray *properties,
375                                gpointer         user_data,
376                                GObject         *chat)
377 {
378 }
379
380 static void
381 tp_chat_get_properties_cb (TpProxy         *proxy,
382                            const GPtrArray *properties,
383                            const GError    *error,
384                            gpointer         user_data,
385                            GObject         *chat)
386 {
387         tp_chat_properties_changed_cb (proxy, properties, user_data, chat);
388 }
389
390 static void
391 tp_chat_list_properties_cb (TpProxy         *proxy,
392                             const GPtrArray *properties,
393                             const GError    *error,
394                             gpointer         user_data,
395                             GObject         *chat)
396 {
397         GArray *ids;
398         guint   i;
399
400         ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), properties->len);
401         for (i = 0; i < properties->len; i++) {
402                 GValueArray *prop_struct;
403                 guint        id;
404
405                 prop_struct = g_ptr_array_index (properties, i);
406                 id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
407                 g_array_append_val (ids, id);
408         }
409
410         tp_cli_properties_interface_call_get_properties (proxy, -1,
411                                                          ids,
412                                                          tp_chat_get_properties_cb,
413                                                          NULL, NULL,
414                                                          chat);
415
416         g_array_free (ids, TRUE);
417 }
418
419 static gboolean
420 tp_chat_channel_ready_cb (EmpathyTpChat *chat)
421 {
422         EmpathyTpChatPriv *priv = GET_PRIV (chat);
423
424         empathy_debug (DEBUG_DOMAIN, "Channel ready");
425
426         tp_cli_channel_type_text_call_list_pending_messages (priv->channel, -1,
427                                                              priv->acknowledge,
428                                                              tp_chat_list_pending_messages_cb,
429                                                              NULL, NULL,
430                                                              G_OBJECT (chat));
431         tp_cli_properties_interface_call_list_properties (priv->channel, -1,
432                                                           tp_chat_list_properties_cb,
433                                                           NULL, NULL,
434                                                           G_OBJECT (chat));
435
436
437         tp_cli_channel_type_text_connect_to_received (priv->channel,
438                                                       tp_chat_received_cb,
439                                                       NULL, NULL,
440                                                       G_OBJECT (chat), NULL);
441         tp_cli_channel_type_text_connect_to_sent (priv->channel,
442                                                   tp_chat_sent_cb,
443                                                   NULL, NULL,
444                                                   G_OBJECT (chat), NULL);
445         tp_cli_channel_type_text_connect_to_send_error (priv->channel,
446                                                         tp_chat_send_error_cb,
447                                                         NULL, NULL,
448                                                         G_OBJECT (chat), NULL);
449         tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv->channel,
450                                                                            tp_chat_state_changed_cb,
451                                                                            NULL, NULL,
452                                                                            G_OBJECT (chat), NULL);
453         tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv->channel,
454                                                                            tp_chat_state_changed_cb,
455                                                                            NULL, NULL,
456                                                                            G_OBJECT (chat), NULL);
457         tp_cli_properties_interface_connect_to_properties_changed (priv->channel,
458                                                                    tp_chat_properties_changed_cb,
459                                                                    NULL, NULL,
460                                                                    G_OBJECT (chat), NULL);
461
462         return FALSE;
463 }
464
465 static void
466 tp_chat_finalize (GObject *object)
467 {
468         EmpathyTpChatPriv *priv = GET_PRIV (object);
469
470         if (priv->acknowledge) {
471                 empathy_debug (DEBUG_DOMAIN, "Closing channel...");
472                 tp_cli_channel_call_close (priv->channel, -1,
473                                            tp_chat_async_cb,
474                                            "closing channel", NULL,
475                                            NULL);
476         }
477
478         if (priv->channel) {
479                 g_signal_handlers_disconnect_by_func (priv->channel,
480                                                       tp_chat_invalidated_cb,
481                                                       object);
482                 g_object_unref (priv->channel);
483         }
484         if (priv->tp_chan) {
485                 g_object_unref (priv->tp_chan);
486         }
487
488         g_object_unref (priv->factory);
489         g_object_unref (priv->user);
490         g_object_unref (priv->account);
491         g_object_unref (priv->mc);
492         g_free (priv->id);
493
494         G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
495 }
496
497 static GObject *
498 tp_chat_constructor (GType                  type,
499                      guint                  n_props,
500                      GObjectConstructParam *props)
501 {
502         GObject           *chat;
503         EmpathyTpChatPriv *priv;
504         gboolean           channel_ready;
505
506         chat = G_OBJECT_CLASS (empathy_tp_chat_parent_class)->constructor (type, n_props, props);
507
508         priv = GET_PRIV (chat);
509         priv->factory = empathy_contact_factory_new ();
510         priv->user = empathy_contact_factory_get_user (priv->factory, priv->account);
511         priv->mc = empathy_mission_control_new ();
512
513         g_signal_connect (priv->channel, "invalidated",
514                           G_CALLBACK (tp_chat_invalidated_cb),
515                           chat);
516
517         g_object_get (priv->channel, "channel-ready", &channel_ready, NULL);
518         if (channel_ready) {
519                 /* FIXME: We do that in a cb to let time to set the acknowledge
520                  * property, this property should be required for construct. */
521                 g_idle_add ((GSourceFunc) tp_chat_channel_ready_cb, chat);
522         } else {
523                 g_signal_connect_swapped (priv->channel, "notify::channel-ready",
524                                           G_CALLBACK (tp_chat_channel_ready_cb),
525                                           chat);
526         }
527
528         return chat;
529 }
530
531 static void
532 tp_chat_get_property (GObject    *object,
533                       guint       param_id,
534                       GValue     *value,
535                       GParamSpec *pspec)
536 {
537         EmpathyTpChatPriv *priv = GET_PRIV (object);
538
539         switch (param_id) {
540         case PROP_ACCOUNT:
541                 g_value_set_object (value, priv->account);
542                 break;
543         case PROP_CHANNEL:
544                 g_value_set_object (value, priv->channel);
545                 break;
546         case PROP_ACKNOWLEDGE:
547                 g_value_set_boolean (value, priv->acknowledge);
548                 break;
549         default:
550                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
551                 break;
552         };
553 }
554
555 static void
556 tp_chat_set_property (GObject      *object,
557                       guint         param_id,
558                       const GValue *value,
559                       GParamSpec   *pspec)
560 {
561         EmpathyTpChatPriv *priv = GET_PRIV (object);
562
563         switch (param_id) {
564         case PROP_ACCOUNT:
565                 priv->account = g_object_ref (g_value_get_object (value));
566                 break;
567         case PROP_CHANNEL:
568                 priv->channel = g_object_ref (g_value_get_object (value));
569                 break;
570         case PROP_ACKNOWLEDGE:
571                 empathy_tp_chat_set_acknowledge (EMPATHY_TP_CHAT (object),
572                                                  g_value_get_boolean (value));
573                 break;
574         default:
575                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
576                 break;
577         };
578 }
579
580 static void
581 empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
582 {
583         GObjectClass *object_class = G_OBJECT_CLASS (klass);
584
585         object_class->finalize = tp_chat_finalize;
586         object_class->constructor = tp_chat_constructor;
587         object_class->get_property = tp_chat_get_property;
588         object_class->set_property = tp_chat_set_property;
589
590         /* Construct properties */
591         g_object_class_install_property (object_class,
592                                          PROP_ACCOUNT,
593                                          g_param_spec_object ("account",
594                                                               "channel Account",
595                                                               "The account associated with the channel",
596                                                               MC_TYPE_ACCOUNT,
597                                                               G_PARAM_READWRITE |
598                                                               G_PARAM_CONSTRUCT_ONLY));
599         g_object_class_install_property (object_class,
600                                          PROP_CHANNEL,
601                                          g_param_spec_object ("channel",
602                                                               "telepathy channel",
603                                                               "The text channel for the chat",
604                                                               TP_TYPE_CHANNEL,
605                                                               G_PARAM_READWRITE |
606                                                               G_PARAM_CONSTRUCT_ONLY));
607
608         g_object_class_install_property (object_class,
609                                          PROP_ACKNOWLEDGE,
610                                          g_param_spec_boolean ("acknowledge",
611                                                                "acknowledge messages",
612                                                                "Wheter or not received messages should be acknowledged",
613                                                                FALSE,
614                                                                G_PARAM_READWRITE |
615                                                                G_PARAM_CONSTRUCT));
616
617         /* Signals */
618         signals[MESSAGE_RECEIVED] =
619                 g_signal_new ("message-received",
620                               G_TYPE_FROM_CLASS (klass),
621                               G_SIGNAL_RUN_LAST,
622                               0,
623                               NULL, NULL,
624                               g_cclosure_marshal_VOID__OBJECT,
625                               G_TYPE_NONE,
626                               1, EMPATHY_TYPE_MESSAGE);
627
628         signals[SEND_ERROR] =
629                 g_signal_new ("send-error",
630                               G_TYPE_FROM_CLASS (klass),
631                               G_SIGNAL_RUN_LAST,
632                               0,
633                               NULL, NULL,
634                               _empathy_marshal_VOID__OBJECT_UINT,
635                               G_TYPE_NONE,
636                               2, EMPATHY_TYPE_MESSAGE, G_TYPE_UINT);
637
638         signals[CHAT_STATE_CHANGED] =
639                 g_signal_new ("chat-state-changed",
640                               G_TYPE_FROM_CLASS (klass),
641                               G_SIGNAL_RUN_LAST,
642                               0,
643                               NULL, NULL,
644                               _empathy_marshal_VOID__OBJECT_UINT,
645                               G_TYPE_NONE,
646                               2, EMPATHY_TYPE_CONTACT, G_TYPE_UINT);
647
648         signals[DESTROY] =
649                 g_signal_new ("destroy",
650                               G_TYPE_FROM_CLASS (klass),
651                               G_SIGNAL_RUN_LAST,
652                               0,
653                               NULL, NULL,
654                               g_cclosure_marshal_VOID__VOID,
655                               G_TYPE_NONE,
656                               0);
657
658         g_type_class_add_private (object_class, sizeof (EmpathyTpChatPriv));
659 }
660
661 static void
662 empathy_tp_chat_init (EmpathyTpChat *chat)
663 {
664 }
665
666 EmpathyTpChat *
667 empathy_tp_chat_new (McAccount *account,
668                      TpChan    *tp_chan)
669 {
670         EmpathyTpChat     *chat;
671         EmpathyTpChatPriv *priv;
672         TpChannel         *channel;
673         TpConnection      *connection;
674         MissionControl    *mc;
675         TpConn            *tp_conn;
676
677         mc = empathy_mission_control_new ();
678         tp_conn = mission_control_get_connection (mc, account, NULL);
679         connection = tp_conn_dup_connection (tp_conn);
680         channel = tp_chan_dup_channel (tp_chan, connection, NULL);
681
682         chat = g_object_new (EMPATHY_TYPE_TP_CHAT, 
683                              "account", account,
684                              "channel", channel,
685                              NULL);
686
687         priv = GET_PRIV (chat);
688         priv->tp_chan = g_object_ref (tp_chan);
689
690         g_object_unref (channel);
691         g_object_unref (tp_conn);
692         g_object_unref (connection);
693         g_object_unref (mc);
694
695         return chat;
696 }
697
698 EmpathyTpChat *
699 empathy_tp_chat_new_with_contact (EmpathyContact *contact)
700 {
701         EmpathyTpChat  *chat;
702         MissionControl *mc;
703         McAccount      *account;
704         TpConn         *tp_conn;
705         TpChan         *text_chan;
706         const gchar    *bus_name;
707         guint           handle;
708
709         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
710
711         mc = empathy_mission_control_new ();
712         account = empathy_contact_get_account (contact);
713
714         if (mission_control_get_connection_status (mc, account, NULL) != 0) {
715                 /* The account is not connected. */
716                 return NULL;
717         }
718
719         tp_conn = mission_control_get_connection (mc, account, NULL);
720         g_return_val_if_fail (tp_conn != NULL, NULL);
721         bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn));
722         handle = empathy_contact_get_handle (contact);
723
724         text_chan = tp_conn_new_channel (tp_get_bus (),
725                                          tp_conn,
726                                          bus_name,
727                                          TP_IFACE_CHANNEL_TYPE_TEXT,
728                                          TP_HANDLE_TYPE_CONTACT,
729                                          handle,
730                                          TRUE);
731
732         chat = empathy_tp_chat_new (account, text_chan);
733
734         g_object_unref (tp_conn);
735         g_object_unref (text_chan);
736         g_object_unref (mc);
737
738         return chat;
739 }
740
741 gboolean
742 empathy_tp_chat_get_acknowledge (EmpathyTpChat *chat)
743 {
744         EmpathyTpChatPriv *priv;
745
746         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), FALSE);
747
748         priv = GET_PRIV (chat);
749
750         return priv->acknowledge;
751 }
752
753 void
754 empathy_tp_chat_set_acknowledge (EmpathyTpChat *chat,
755                                  gboolean       acknowledge)
756 {
757         EmpathyTpChatPriv *priv;
758
759         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
760
761         priv = GET_PRIV (chat);
762
763         priv->acknowledge = acknowledge;
764         g_object_notify (G_OBJECT (chat), "acknowledge");
765 }
766
767 TpChan *
768 empathy_tp_chat_get_channel (EmpathyTpChat *chat)
769 {
770         EmpathyTpChatPriv *priv;
771
772         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
773
774         priv = GET_PRIV (chat);
775
776         return priv->tp_chan;
777 }
778
779 McAccount *
780 empathy_tp_chat_get_account (EmpathyTpChat *chat)
781 {
782         EmpathyTpChatPriv *priv;
783
784         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
785
786         priv = GET_PRIV (chat);
787
788         return priv->account;
789 }
790
791 void
792 empathy_tp_chat_send (EmpathyTpChat *chat,
793                       EmpathyMessage *message)
794 {
795         EmpathyTpChatPriv  *priv;
796         const gchar        *message_body;
797         EmpathyMessageType  message_type;
798
799         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
800         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
801
802         priv = GET_PRIV (chat);
803
804         message_body = empathy_message_get_body (message);
805         message_type = empathy_message_get_type (message);
806
807         empathy_debug (DEBUG_DOMAIN, "Sending message: %s", message_body);
808         tp_cli_channel_type_text_call_send (priv->channel, -1,
809                                             message_type,
810                                             message_body,
811                                             tp_chat_async_cb,
812                                             "sending message", NULL,
813                                             G_OBJECT (chat));
814 }
815
816 void
817 empathy_tp_chat_set_state (EmpathyTpChat      *chat,
818                            TpChannelChatState  state)
819 {
820         EmpathyTpChatPriv *priv;
821
822         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
823
824         priv = GET_PRIV (chat);
825
826         empathy_debug (DEBUG_DOMAIN, "Set state: %d", state);
827         tp_cli_channel_interface_chat_state_call_set_chat_state (priv->channel, -1,
828                                                                  state,
829                                                                  tp_chat_async_cb,
830                                                                  "setting chat state",
831                                                                  NULL,
832                                                                  G_OBJECT (chat));
833 }
834
835 const gchar *
836 empathy_tp_chat_get_id (EmpathyTpChat *chat)
837 {
838         EmpathyTpChatPriv *priv;
839
840         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
841
842         priv = GET_PRIV (chat);
843
844         if (!priv->id) {
845                 priv->id = empathy_inspect_channel (priv->account, priv->tp_chan);
846         }
847
848         return priv->id;
849 }
850