1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007-2008 Collabora Ltd.
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.
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.
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
19 * Authors: Xavier Claessens <xclaesse@gmail.com>
26 #include <telepathy-glib/channel.h>
27 #include <telepathy-glib/dbus.h>
28 #include <telepathy-glib/util.h>
30 #include "empathy-tp-chat.h"
31 #include "empathy-tp-group.h"
32 #include "empathy-contact-factory.h"
33 #include "empathy-contact-monitor.h"
34 #include "empathy-contact-list.h"
35 #include "empathy-marshal.h"
36 #include "empathy-time.h"
37 #include "empathy-utils.h"
39 #define DEBUG_FLAG EMPATHY_DEBUG_TP | EMPATHY_DEBUG_CHAT
40 #include "empathy-debug.h"
42 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpChat)
44 gboolean dispose_has_run;
45 EmpathyContactFactory *factory;
46 EmpathyContactMonitor *contact_monitor;
48 EmpathyContact *remote_contact;
49 EmpathyTpGroup *group;
52 gboolean listing_pending_messages;
53 /* Queue of messages not signalled yet */
54 GQueue *messages_queue;
55 /* Queue of messages signalled but not acked yet */
56 GQueue *pending_messages_queue;
57 gboolean had_properties_list;
58 GPtrArray *properties;
66 TpPropertyFlags flags;
70 static void tp_chat_iface_init (EmpathyContactListIface *iface);
88 static guint signals[LAST_SIGNAL];
90 G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat, empathy_tp_chat, G_TYPE_OBJECT,
91 G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
94 static void acknowledge_messages (EmpathyTpChat *chat, GArray *ids);
97 tp_chat_invalidated_cb (TpProxy *proxy,
103 EmpathyTpChatPriv *priv = GET_PRIV (chat);
105 DEBUG ("Channel invalidated: %s", message);
106 g_signal_emit (chat, signals[DESTROY], 0);
108 g_object_unref (priv->channel);
109 priv->channel = NULL;
113 tp_chat_async_cb (TpChannel *proxy,
116 GObject *weak_object)
119 DEBUG ("Error %s: %s", (gchar*) user_data, error->message);
124 tp_chat_member_added_cb (EmpathyTpGroup *group,
125 EmpathyContact *contact,
126 EmpathyContact *actor,
128 const gchar *message,
131 EmpathyTpChatPriv *priv = GET_PRIV (chat);
132 guint handle_type = 0;
134 if (priv->channel == NULL)
137 priv->members_count++;
138 g_signal_emit_by_name (chat, "members-changed",
139 contact, actor, reason, message,
142 g_object_get (priv->channel, "handle-type", &handle_type, NULL);
143 if (handle_type == TP_HANDLE_TYPE_ROOM) {
147 if (priv->members_count > 2 && priv->remote_contact) {
148 /* We now have more than 2 members, this is not a p2p chat
149 * anymore. Remove the remote-contact as it makes no sense, the
150 * EmpathyContactList interface must be used now. */
151 g_object_unref (priv->remote_contact);
152 priv->remote_contact = NULL;
153 g_object_notify (G_OBJECT (chat), "remote-contact");
155 if (priv->members_count <= 2 && !priv->remote_contact &&
156 !empathy_contact_is_user (contact)) {
157 /* This is a p2p chat, if it's not ourself that means this is
158 * the remote contact with who we are chatting. This is to
159 * avoid forcing the usage of the EmpathyContactList interface
161 priv->remote_contact = g_object_ref (contact);
162 g_object_notify (G_OBJECT (chat), "remote-contact");
167 tp_chat_member_removed_cb (EmpathyTpGroup *group,
168 EmpathyContact *contact,
169 EmpathyContact *actor,
171 const gchar *message,
174 EmpathyTpChatPriv *priv = GET_PRIV (chat);
175 guint handle_type = 0;
177 if (priv->channel == NULL)
180 priv->members_count--;
181 g_signal_emit_by_name (chat, "members-changed",
182 contact, actor, reason, message,
185 g_object_get (priv->channel, "handle-type", &handle_type, NULL);
186 if (handle_type == TP_HANDLE_TYPE_ROOM) {
190 if (priv->members_count <= 2 && !priv->remote_contact) {
193 /* We are not a MUC anymore, get the remote contact back */
194 members = empathy_tp_group_get_members (group);
195 for (l = members; l; l = l->next) {
196 if (!empathy_contact_is_user (l->data)) {
197 priv->remote_contact = g_object_ref (l->data);
198 g_object_notify (G_OBJECT (chat), "remote-contact");
202 g_list_foreach (members, (GFunc) g_object_unref, NULL);
203 g_list_free (members);
208 tp_chat_local_pending_cb (EmpathyTpGroup *group,
209 EmpathyContact *contact,
210 EmpathyContact *actor,
212 const gchar *message,
215 EmpathyTpChatPriv *priv = GET_PRIV (chat);
217 if (priv->channel == NULL)
220 g_signal_emit_by_name (chat, "pendings-changed",
221 contact, actor, reason, message,
226 tp_chat_add (EmpathyContactList *list,
227 EmpathyContact *contact,
228 const gchar *message)
230 EmpathyTpChatPriv *priv = GET_PRIV (list);
232 GArray handles = {(gchar *) &handle, 1};
234 g_return_if_fail (EMPATHY_IS_TP_CHAT (list));
235 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
237 handle = empathy_contact_get_handle (contact);
238 tp_cli_channel_interface_group_call_add_members (priv->channel, -1,
245 tp_chat_remove (EmpathyContactList *list,
246 EmpathyContact *contact,
247 const gchar *message)
249 EmpathyTpChatPriv *priv = GET_PRIV (list);
251 GArray handles = {(gchar *) &handle, 1};
253 g_return_if_fail (EMPATHY_IS_TP_CHAT (list));
254 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
256 handle = empathy_contact_get_handle (contact);
257 tp_cli_channel_interface_group_call_remove_members (priv->channel, -1,
264 tp_chat_get_members (EmpathyContactList *list)
266 EmpathyTpChatPriv *priv = GET_PRIV (list);
267 GList *members = NULL;
269 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list), NULL);
272 members = empathy_tp_group_get_members (priv->group);
274 members = g_list_prepend (members, g_object_ref (priv->user));
275 members = g_list_prepend (members, g_object_ref (priv->remote_contact));
281 static EmpathyContactMonitor *
282 tp_chat_get_monitor (EmpathyContactList *list)
284 EmpathyTpChatPriv *priv;
286 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list), NULL);
288 priv = GET_PRIV (list);
290 if (priv->contact_monitor == NULL) {
291 priv->contact_monitor = empathy_contact_monitor_new_for_iface (list);
294 return priv->contact_monitor;
297 static EmpathyMessage *
298 tp_chat_build_message (EmpathyTpChat *chat,
303 const gchar *message_body)
305 EmpathyTpChatPriv *priv;
306 EmpathyMessage *message;
307 EmpathyContact *sender;
309 priv = GET_PRIV (chat);
311 if (from_handle == 0) {
312 sender = g_object_ref (priv->user);
314 sender = empathy_contact_factory_get_from_handle (priv->factory,
319 message = empathy_message_new (message_body);
320 empathy_message_set_tptype (message, type);
321 empathy_message_set_sender (message, sender);
322 empathy_message_set_receiver (message, priv->user);
323 empathy_message_set_timestamp (message, timestamp);
324 empathy_message_set_id (message, id);
326 g_object_unref (sender);
332 tp_chat_sender_ready_notify_cb (EmpathyContact *contact,
333 GParamSpec *param_spec,
336 EmpathyTpChatPriv *priv = GET_PRIV (chat);
337 EmpathyMessage *message;
338 EmpathyContactReady ready;
339 EmpathyContact *sender = NULL;
340 gboolean removed = FALSE;
342 /* Emit all messages queued until we find a message with not
343 * ready sender (in case of a MUC we could have more than one sender).
344 * When leaving this loop, sender is the first not ready contact queued
345 * and removed tells if at least one message got removed
347 while ((message = g_queue_peek_head (priv->messages_queue)) != NULL) {
348 sender = empathy_message_get_sender (message);
349 ready = empathy_contact_get_ready (sender);
351 if ((ready & EMPATHY_CONTACT_READY_NAME) == 0 ||
352 (ready & EMPATHY_CONTACT_READY_ID) == 0) {
356 DEBUG ("Queued message ready");
357 message = g_queue_pop_head (priv->messages_queue);
358 g_queue_push_tail (priv->pending_messages_queue, message);
359 g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
364 /* We removed at least one message from the queue, disconnect
365 * the ready signal from the previous contact */
366 g_signal_handlers_disconnect_by_func (contact,
367 tp_chat_sender_ready_notify_cb,
370 if (g_queue_get_length (priv->messages_queue) > 0) {
371 /* We still have queued message, connect the ready
372 * signal on the new first message sender. */
373 g_signal_connect (sender, "notify::ready",
374 G_CALLBACK (tp_chat_sender_ready_notify_cb),
381 tp_chat_emit_or_queue_message (EmpathyTpChat *chat,
382 EmpathyMessage *message)
384 EmpathyTpChatPriv *priv = GET_PRIV (chat);
385 EmpathyContact *sender;
386 EmpathyContactReady ready;
388 if (g_queue_get_length (priv->messages_queue) > 0) {
389 DEBUG ("Message queue not empty");
390 g_queue_push_tail (priv->messages_queue, g_object_ref (message));
395 sender = empathy_message_get_sender (message);
396 ready = empathy_contact_get_ready (sender);
397 if ((ready & EMPATHY_CONTACT_READY_NAME) &&
398 (ready & EMPATHY_CONTACT_READY_ID)) {
399 DEBUG ("Message queue empty and sender ready");
400 g_queue_push_tail (priv->pending_messages_queue, g_object_ref (message));
401 g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
405 DEBUG ("Sender not ready");
406 g_queue_push_tail (priv->messages_queue, g_object_ref (message));
407 g_signal_connect (sender, "notify::ready",
408 G_CALLBACK (tp_chat_sender_ready_notify_cb),
413 tp_chat_received_cb (TpChannel *channel,
419 const gchar *message_body,
423 EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_);
424 EmpathyTpChatPriv *priv = GET_PRIV (chat);
425 EmpathyMessage *message;
427 if (priv->channel == NULL)
430 if (priv->listing_pending_messages) {
434 DEBUG ("Message received: %s", message_body);
436 if (message_flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT &&
437 !tp_strdiff (message_body, "")) {
440 DEBUG ("Empty message with NonTextContent, ignoring and acking.");
442 ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
443 g_array_append_val (ids, message_id);
444 acknowledge_messages (chat, ids);
445 g_array_free (ids, TRUE);
450 message = tp_chat_build_message (chat,
457 tp_chat_emit_or_queue_message (chat, message);
458 g_object_unref (message);
462 tp_chat_sent_cb (TpChannel *channel,
465 const gchar *message_body,
469 EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_);
470 EmpathyTpChatPriv *priv = GET_PRIV (chat);
471 EmpathyMessage *message;
473 if (priv->channel == NULL)
476 DEBUG ("Message sent: %s", message_body);
478 message = tp_chat_build_message (chat,
485 tp_chat_emit_or_queue_message (chat, message);
486 g_object_unref (message);
490 tp_chat_send_error_cb (TpChannel *channel,
494 const gchar *message_body,
498 EmpathyMessage *message;
499 EmpathyTpChatPriv *priv = GET_PRIV (chat);
501 if (priv->channel == NULL)
504 DEBUG ("Message sent error: %s (%d)", message_body, error_code);
506 message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
513 g_signal_emit (chat, signals[SEND_ERROR], 0, message, error_code);
514 g_object_unref (message);
518 tp_chat_send_cb (TpChannel *proxy,
523 EmpathyMessage *message = EMPATHY_MESSAGE (user_data);
526 DEBUG ("Error: %s", error->message);
527 g_signal_emit (chat, signals[SEND_ERROR], 0, message,
528 TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN);
533 tp_chat_state_changed_cb (TpChannel *channel,
539 EmpathyTpChatPriv *priv = GET_PRIV (chat);
540 EmpathyContact *contact;
542 if (priv->channel == NULL)
545 contact = empathy_contact_factory_get_from_handle (priv->factory,
549 DEBUG ("Chat state changed for %s (%d): %d",
550 empathy_contact_get_name (contact), handle, state);
552 g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
553 g_object_unref (contact);
557 tp_chat_list_pending_messages_cb (TpChannel *channel,
558 const GPtrArray *messages_list,
563 EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_);
564 EmpathyTpChatPriv *priv = GET_PRIV (chat);
566 GArray *empty_non_text_content_ids = NULL;
568 priv->listing_pending_messages = FALSE;
570 if (priv->channel == NULL)
574 DEBUG ("Error listing pending messages: %s", error->message);
578 for (i = 0; i < messages_list->len; i++) {
579 EmpathyMessage *message;
580 GValueArray *message_struct;
581 const gchar *message_body;
588 message_struct = g_ptr_array_index (messages_list, i);
590 message_id = g_value_get_uint (g_value_array_get_nth (message_struct, 0));
591 timestamp = g_value_get_uint (g_value_array_get_nth (message_struct, 1));
592 from_handle = g_value_get_uint (g_value_array_get_nth (message_struct, 2));
593 message_type = g_value_get_uint (g_value_array_get_nth (message_struct, 3));
594 message_flags = g_value_get_uint (g_value_array_get_nth (message_struct, 4));
595 message_body = g_value_get_string (g_value_array_get_nth (message_struct, 5));
597 DEBUG ("Message pending: %s", message_body);
599 if (message_flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT &&
600 !tp_strdiff (message_body, "")) {
601 DEBUG ("Empty message with NonTextContent, ignoring and acking.");
603 if (empty_non_text_content_ids == NULL) {
604 empty_non_text_content_ids = g_array_new (FALSE, FALSE, sizeof (guint));
607 g_array_append_val (empty_non_text_content_ids, message_id);
611 message = tp_chat_build_message (chat,
618 tp_chat_emit_or_queue_message (chat, message);
619 g_object_unref (message);
622 if (empty_non_text_content_ids != NULL) {
623 acknowledge_messages (chat, empty_non_text_content_ids);
624 g_array_free (empty_non_text_content_ids, TRUE);
629 tp_chat_property_flags_changed_cb (TpProxy *proxy,
630 const GPtrArray *properties,
634 EmpathyTpChatPriv *priv = GET_PRIV (chat);
637 if (priv->channel == NULL)
640 if (!priv->had_properties_list || !properties) {
644 for (i = 0; i < properties->len; i++) {
645 GValueArray *prop_struct;
646 TpChatProperty *property;
650 prop_struct = g_ptr_array_index (properties, i);
651 id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
652 flags = g_value_get_uint (g_value_array_get_nth (prop_struct, 1));
654 for (j = 0; j < priv->properties->len; j++) {
655 property = g_ptr_array_index (priv->properties, j);
656 if (property->id == id) {
657 property->flags = flags;
658 DEBUG ("property %s flags changed: %d",
659 property->name, property->flags);
667 tp_chat_properties_changed_cb (TpProxy *proxy,
668 const GPtrArray *properties,
672 EmpathyTpChatPriv *priv = GET_PRIV (chat);
675 if (priv->channel == NULL)
678 if (!priv->had_properties_list || !properties) {
682 for (i = 0; i < properties->len; i++) {
683 GValueArray *prop_struct;
684 TpChatProperty *property;
688 prop_struct = g_ptr_array_index (properties, i);
689 id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
690 src_value = g_value_get_boxed (g_value_array_get_nth (prop_struct, 1));
692 for (j = 0; j < priv->properties->len; j++) {
693 property = g_ptr_array_index (priv->properties, j);
694 if (property->id == id) {
695 if (property->value) {
696 g_value_copy (src_value, property->value);
698 property->value = tp_g_value_slice_dup (src_value);
701 DEBUG ("property %s changed", property->name);
702 g_signal_emit (chat, signals[PROPERTY_CHANGED], 0,
703 property->name, property->value);
711 tp_chat_get_properties_cb (TpProxy *proxy,
712 const GPtrArray *properties,
718 DEBUG ("Error getting properties: %s", error->message);
722 tp_chat_properties_changed_cb (proxy, properties, user_data, chat);
726 tp_chat_list_properties_cb (TpProxy *proxy,
727 const GPtrArray *properties,
732 EmpathyTpChatPriv *priv = GET_PRIV (chat);
736 if (priv->channel == NULL)
739 priv->had_properties_list = TRUE;
742 DEBUG ("Error listing properties: %s", error->message);
746 ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), properties->len);
747 priv->properties = g_ptr_array_sized_new (properties->len);
748 for (i = 0; i < properties->len; i++) {
749 GValueArray *prop_struct;
750 TpChatProperty *property;
752 prop_struct = g_ptr_array_index (properties, i);
753 property = g_slice_new0 (TpChatProperty);
754 property->id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
755 property->name = g_value_dup_string (g_value_array_get_nth (prop_struct, 1));
756 property->flags = g_value_get_uint (g_value_array_get_nth (prop_struct, 3));
758 DEBUG ("Adding property name=%s id=%d flags=%d",
759 property->name, property->id, property->flags);
760 g_ptr_array_add (priv->properties, property);
761 if (property->flags & TP_PROPERTY_FLAG_READ) {
762 g_array_append_val (ids, property->id);
766 tp_cli_properties_interface_call_get_properties (proxy, -1,
768 tp_chat_get_properties_cb,
772 g_array_free (ids, TRUE);
776 empathy_tp_chat_set_property (EmpathyTpChat *chat,
780 EmpathyTpChatPriv *priv = GET_PRIV (chat);
781 TpChatProperty *property;
784 g_return_if_fail (priv->ready);
786 for (i = 0; i < priv->properties->len; i++) {
787 property = g_ptr_array_index (priv->properties, i);
788 if (!tp_strdiff (property->name, name)) {
789 GPtrArray *properties;
792 GValue dest_value = {0, };
794 if (!(property->flags & TP_PROPERTY_FLAG_WRITE)) {
798 g_value_init (&id, G_TYPE_UINT);
799 g_value_init (&dest_value, G_TYPE_VALUE);
800 g_value_set_uint (&id, property->id);
801 g_value_set_boxed (&dest_value, value);
803 prop = g_value_array_new (2);
804 g_value_array_append (prop, &id);
805 g_value_array_append (prop, &dest_value);
807 properties = g_ptr_array_sized_new (1);
808 g_ptr_array_add (properties, prop);
810 DEBUG ("Set property %s", name);
811 tp_cli_properties_interface_call_set_properties (priv->channel, -1,
813 (tp_cli_properties_interface_callback_for_set_properties)
815 "Seting property", NULL,
818 g_ptr_array_free (properties, TRUE);
819 g_value_array_free (prop);
827 tp_chat_channel_ready_cb (EmpathyTpChat *chat)
829 EmpathyTpChatPriv *priv = GET_PRIV (chat);
830 TpConnection *connection;
831 guint handle, handle_type;
833 if (priv->channel == NULL)
836 DEBUG ("Channel ready");
838 g_object_get (priv->channel,
839 "connection", &connection,
841 "handle_type", &handle_type,
844 if (handle_type == TP_HANDLE_TYPE_CONTACT && handle != 0) {
845 priv->remote_contact = empathy_contact_factory_get_from_handle (priv->factory,
848 g_object_notify (G_OBJECT (chat), "remote-contact");
851 if (tp_proxy_has_interface_by_id (priv->channel,
852 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP)) {
853 priv->group = empathy_tp_group_new (priv->channel);
855 g_signal_connect (priv->group, "member-added",
856 G_CALLBACK (tp_chat_member_added_cb),
858 g_signal_connect (priv->group, "member-removed",
859 G_CALLBACK (tp_chat_member_removed_cb),
861 g_signal_connect (priv->group, "local-pending",
862 G_CALLBACK (tp_chat_local_pending_cb),
864 empathy_run_until_ready (priv->group);
866 priv->members_count = 2;
869 if (tp_proxy_has_interface_by_id (priv->channel,
870 TP_IFACE_QUARK_PROPERTIES_INTERFACE)) {
871 tp_cli_properties_interface_call_list_properties (priv->channel, -1,
872 tp_chat_list_properties_cb,
875 tp_cli_properties_interface_connect_to_properties_changed (priv->channel,
876 tp_chat_properties_changed_cb,
878 G_OBJECT (chat), NULL);
879 tp_cli_properties_interface_connect_to_property_flags_changed (priv->channel,
880 tp_chat_property_flags_changed_cb,
882 G_OBJECT (chat), NULL);
885 priv->listing_pending_messages = TRUE;
886 tp_cli_channel_type_text_call_list_pending_messages (priv->channel, -1,
888 tp_chat_list_pending_messages_cb,
892 tp_cli_channel_type_text_connect_to_received (priv->channel,
895 G_OBJECT (chat), NULL);
896 tp_cli_channel_type_text_connect_to_sent (priv->channel,
899 G_OBJECT (chat), NULL);
900 tp_cli_channel_type_text_connect_to_send_error (priv->channel,
901 tp_chat_send_error_cb,
903 G_OBJECT (chat), NULL);
904 tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv->channel,
905 tp_chat_state_changed_cb,
907 G_OBJECT (chat), NULL);
908 tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv->channel,
909 tp_chat_state_changed_cb,
911 G_OBJECT (chat), NULL);
914 g_object_notify (G_OBJECT (chat), "ready");
918 tp_chat_dispose (GObject *object)
920 EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
921 EmpathyTpChatPriv *priv = GET_PRIV (self);
923 if (priv->dispose_has_run)
926 priv->dispose_has_run = TRUE;
928 if (priv->channel != NULL)
930 g_signal_handlers_disconnect_by_func (priv->channel,
931 tp_chat_invalidated_cb, self);
932 g_object_unref (priv->channel);
933 priv->channel = NULL;
936 if (priv->remote_contact != NULL)
937 g_object_unref (priv->remote_contact);
939 priv->remote_contact = NULL;
941 if (priv->group != NULL)
942 g_object_unref (priv->group);
945 if (priv->factory != NULL)
946 g_object_unref (priv->factory);
947 priv->factory = NULL;
949 if (priv->user != NULL);
950 g_object_unref (priv->user);
953 if (priv->account != NULL);
954 g_object_unref (priv->account);
955 priv->account = NULL;
957 if (priv->contact_monitor)
958 g_object_unref (priv->contact_monitor);
959 priv->contact_monitor = NULL;
961 if (!g_queue_is_empty (priv->messages_queue)) {
962 EmpathyMessage *message;
963 EmpathyContact *contact;
965 message = g_queue_peek_head (priv->messages_queue);
966 contact = empathy_message_get_sender (message);
967 g_signal_handlers_disconnect_by_func (contact,
968 tp_chat_sender_ready_notify_cb, object);
971 g_list_foreach (priv->messages_queue->head,
972 (GFunc) g_object_unref, NULL);
974 g_list_foreach (priv->pending_messages_queue->head,
975 (GFunc) g_object_unref, NULL);
977 if (G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose)
978 G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose (object);
982 tp_chat_finalize (GObject *object)
984 EmpathyTpChatPriv *priv = GET_PRIV (object);
987 DEBUG ("Finalize: %p", object);
989 if (priv->properties) {
990 for (i = 0; i < priv->properties->len; i++) {
991 TpChatProperty *property;
993 property = g_ptr_array_index (priv->properties, i);
994 g_free (property->name);
995 if (property->value) {
996 tp_g_value_slice_free (property->value);
998 g_slice_free (TpChatProperty, property);
1000 g_ptr_array_free (priv->properties, TRUE);
1003 g_queue_free (priv->messages_queue);
1004 g_queue_free (priv->pending_messages_queue);
1006 G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
1010 tp_chat_constructor (GType type,
1012 GObjectConstructParam *props)
1015 EmpathyTpChatPriv *priv;
1016 gboolean channel_ready;
1018 chat = G_OBJECT_CLASS (empathy_tp_chat_parent_class)->constructor (type, n_props, props);
1020 priv = GET_PRIV (chat);
1021 priv->account = empathy_channel_get_account (priv->channel);
1022 priv->factory = empathy_contact_factory_dup_singleton ();
1023 priv->user = empathy_contact_factory_get_user (priv->factory, priv->account);
1025 g_signal_connect (priv->channel, "invalidated",
1026 G_CALLBACK (tp_chat_invalidated_cb),
1029 g_object_get (priv->channel, "channel-ready", &channel_ready, NULL);
1030 if (channel_ready) {
1031 tp_chat_channel_ready_cb (EMPATHY_TP_CHAT (chat));
1033 g_signal_connect_swapped (priv->channel, "notify::channel-ready",
1034 G_CALLBACK (tp_chat_channel_ready_cb),
1042 tp_chat_get_property (GObject *object,
1047 EmpathyTpChatPriv *priv = GET_PRIV (object);
1051 g_value_set_object (value, priv->channel);
1053 case PROP_REMOTE_CONTACT:
1054 g_value_set_object (value, priv->remote_contact);
1057 g_value_set_boolean (value, priv->ready);
1060 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1066 tp_chat_set_property (GObject *object,
1068 const GValue *value,
1071 EmpathyTpChatPriv *priv = GET_PRIV (object);
1075 priv->channel = g_value_dup_object (value);
1078 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1084 empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
1086 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1088 object_class->dispose = tp_chat_dispose;
1089 object_class->finalize = tp_chat_finalize;
1090 object_class->constructor = tp_chat_constructor;
1091 object_class->get_property = tp_chat_get_property;
1092 object_class->set_property = tp_chat_set_property;
1094 g_object_class_install_property (object_class,
1096 g_param_spec_object ("channel",
1097 "telepathy channel",
1098 "The text channel for the chat",
1101 G_PARAM_CONSTRUCT_ONLY));
1103 g_object_class_install_property (object_class,
1104 PROP_REMOTE_CONTACT,
1105 g_param_spec_object ("remote-contact",
1106 "The remote contact",
1107 "The remote contact if there is no group iface on the channel",
1108 EMPATHY_TYPE_CONTACT,
1110 g_object_class_install_property (object_class,
1112 g_param_spec_boolean ("ready",
1113 "Is the object ready",
1114 "This object can't be used until this becomes true",
1119 signals[MESSAGE_RECEIVED] =
1120 g_signal_new ("message-received",
1121 G_TYPE_FROM_CLASS (klass),
1125 g_cclosure_marshal_VOID__OBJECT,
1127 1, EMPATHY_TYPE_MESSAGE);
1129 signals[SEND_ERROR] =
1130 g_signal_new ("send-error",
1131 G_TYPE_FROM_CLASS (klass),
1135 _empathy_marshal_VOID__OBJECT_UINT,
1137 2, EMPATHY_TYPE_MESSAGE, G_TYPE_UINT);
1139 signals[CHAT_STATE_CHANGED] =
1140 g_signal_new ("chat-state-changed",
1141 G_TYPE_FROM_CLASS (klass),
1145 _empathy_marshal_VOID__OBJECT_UINT,
1147 2, EMPATHY_TYPE_CONTACT, G_TYPE_UINT);
1149 signals[PROPERTY_CHANGED] =
1150 g_signal_new ("property-changed",
1151 G_TYPE_FROM_CLASS (klass),
1155 _empathy_marshal_VOID__STRING_BOXED,
1157 2, G_TYPE_STRING, G_TYPE_VALUE);
1160 g_signal_new ("destroy",
1161 G_TYPE_FROM_CLASS (klass),
1165 g_cclosure_marshal_VOID__VOID,
1169 g_type_class_add_private (object_class, sizeof (EmpathyTpChatPriv));
1173 empathy_tp_chat_init (EmpathyTpChat *chat)
1175 EmpathyTpChatPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (chat,
1176 EMPATHY_TYPE_TP_CHAT, EmpathyTpChatPriv);
1179 priv->contact_monitor = NULL;
1180 priv->messages_queue = g_queue_new ();
1181 priv->pending_messages_queue = g_queue_new ();
1185 tp_chat_iface_init (EmpathyContactListIface *iface)
1187 iface->add = tp_chat_add;
1188 iface->remove = tp_chat_remove;
1189 iface->get_members = tp_chat_get_members;
1190 iface->get_monitor = tp_chat_get_monitor;
1194 empathy_tp_chat_new (TpChannel *channel)
1196 return g_object_new (EMPATHY_TYPE_TP_CHAT,
1202 empathy_tp_chat_close (EmpathyTpChat *chat) {
1203 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1205 /* If there are still messages left, it'll come back..
1206 We loose the ordering of sent messages though */
1207 g_signal_handlers_disconnect_by_func (priv->channel,
1208 tp_chat_invalidated_cb, chat);
1210 tp_cli_channel_call_close (priv->channel, -1, tp_chat_async_cb,
1211 "closing channel", NULL, NULL);
1213 g_object_unref (priv->channel);
1214 priv->channel = NULL;
1216 g_signal_emit (chat, signals[DESTROY], 0);
1220 empathy_tp_chat_get_id (EmpathyTpChat *chat)
1222 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1224 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
1225 g_return_val_if_fail (priv->ready, NULL);
1227 return tp_channel_get_identifier (priv->channel);
1231 empathy_tp_chat_get_remote_contact (EmpathyTpChat *chat)
1233 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1235 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
1237 return priv->remote_contact;
1241 empathy_tp_chat_get_account (EmpathyTpChat *chat)
1243 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1245 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), FALSE);
1247 return priv->account;
1251 empathy_tp_chat_get_channel (EmpathyTpChat *chat)
1253 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1255 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
1257 return priv->channel;
1261 empathy_tp_chat_is_ready (EmpathyTpChat *chat)
1263 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1265 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), FALSE);
1271 empathy_tp_chat_get_members_count (EmpathyTpChat *chat)
1273 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1275 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), 0);
1277 return priv->members_count;
1281 empathy_tp_chat_send (EmpathyTpChat *chat,
1282 EmpathyMessage *message)
1284 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1285 const gchar *message_body;
1286 TpChannelTextMessageType message_type;
1288 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
1289 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
1290 g_return_if_fail (priv->ready);
1292 message_body = empathy_message_get_body (message);
1293 message_type = empathy_message_get_tptype (message);
1295 DEBUG ("Sending message: %s", message_body);
1296 tp_cli_channel_type_text_call_send (priv->channel, -1,
1300 g_object_ref (message),
1301 (GDestroyNotify) g_object_unref,
1306 empathy_tp_chat_set_state (EmpathyTpChat *chat,
1307 TpChannelChatState state)
1309 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1311 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
1312 g_return_if_fail (priv->ready);
1314 DEBUG ("Set state: %d", state);
1315 tp_cli_channel_interface_chat_state_call_set_chat_state (priv->channel, -1,
1318 "setting chat state",
1325 empathy_tp_chat_get_pending_messages (EmpathyTpChat *chat)
1327 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1329 return priv->pending_messages_queue->head;
1333 acknowledge_messages (EmpathyTpChat *chat, GArray *ids) {
1334 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1336 tp_cli_channel_type_text_call_acknowledge_pending_messages (
1337 priv->channel, -1, ids, tp_chat_async_cb,
1338 "acknowledging received message", NULL, G_OBJECT (chat));
1342 empathy_tp_chat_acknowledge_message (EmpathyTpChat *chat,
1343 EmpathyMessage *message) {
1344 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1345 GArray *message_ids;
1349 if (empathy_message_get_sender (message) == priv->user)
1352 message_ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
1354 id = empathy_message_get_id (message);
1355 g_array_append_val (message_ids, id);
1356 acknowledge_messages (chat, message_ids);
1357 g_array_free (message_ids, TRUE);
1360 m = g_queue_find (priv->pending_messages_queue, message);
1361 g_assert (m != NULL);
1362 g_queue_delete_link (priv->pending_messages_queue, m);
1363 g_object_unref (message);
1367 empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat,
1368 const GList *messages) {
1369 EmpathyTpChatPriv *priv = GET_PRIV (chat);
1370 /* Copy messages as the messges list (probably is) our own */
1371 GList *msgs = g_list_copy ((GList *) messages);
1374 GArray *message_ids;
1376 length = g_list_length ((GList *)messages);
1381 message_ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), length);
1383 for (l = msgs; l != NULL; l = g_list_next (l)) {
1386 EmpathyMessage *message = EMPATHY_MESSAGE (l->data);
1388 m = g_queue_find (priv->pending_messages_queue, message);
1389 g_assert (m != NULL);
1390 g_queue_delete_link (priv->pending_messages_queue, m);
1392 if (empathy_message_get_sender (message) != priv->user) {
1393 guint id = empathy_message_get_id (message);
1394 g_array_append_val (message_ids, id);
1398 if (message_ids->len > 0)
1399 acknowledge_messages (chat, message_ids);
1401 g_array_free (message_ids, TRUE);