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-contact-factory.h"
32 #include "empathy-contact-monitor.h"
33 #include "empathy-contact-list.h"
34 #include "empathy-marshal.h"
35 #include "empathy-time.h"
36 #include "empathy-utils.h"
38 #define DEBUG_FLAG EMPATHY_DEBUG_TP | EMPATHY_DEBUG_CHAT
39 #include "empathy-debug.h"
41 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpChat)
43 gboolean dispose_has_run
;
44 EmpathyTpContactFactory
*factory
;
45 EmpathyContactMonitor
*contact_monitor
;
47 EmpathyContact
*remote_contact
;
50 gboolean listing_pending_messages
;
51 /* Queue of messages not signalled yet */
52 GQueue
*messages_queue
;
53 /* Queue of messages signalled but not acked yet */
54 GQueue
*pending_messages_queue
;
55 gboolean had_properties_list
;
56 GPtrArray
*properties
;
63 TpPropertyFlags flags
;
67 static void tp_chat_iface_init (EmpathyContactListIface
*iface
);
85 static guint signals
[LAST_SIGNAL
];
87 G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat
, empathy_tp_chat
, G_TYPE_OBJECT
,
88 G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST
,
91 static void acknowledge_messages (EmpathyTpChat
*chat
, GArray
*ids
);
94 tp_chat_invalidated_cb (TpProxy
*proxy
,
100 DEBUG ("Channel invalidated: %s", message
);
101 g_signal_emit (chat
, signals
[DESTROY
], 0);
105 tp_chat_async_cb (TpChannel
*proxy
,
108 GObject
*weak_object
)
111 DEBUG ("Error %s: %s", (gchar
*) user_data
, error
->message
);
116 tp_chat_add (EmpathyContactList
*list
,
117 EmpathyContact
*contact
,
118 const gchar
*message
)
120 EmpathyTpChatPriv
*priv
= GET_PRIV (list
);
122 GArray handles
= {(gchar
*) &handle
, 1};
124 g_return_if_fail (EMPATHY_IS_TP_CHAT (list
));
125 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
127 handle
= empathy_contact_get_handle (contact
);
128 tp_cli_channel_interface_group_call_add_members (priv
->channel
, -1,
135 tp_chat_remove (EmpathyContactList
*list
,
136 EmpathyContact
*contact
,
137 const gchar
*message
)
139 EmpathyTpChatPriv
*priv
= GET_PRIV (list
);
141 GArray handles
= {(gchar
*) &handle
, 1};
143 g_return_if_fail (EMPATHY_IS_TP_CHAT (list
));
144 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
146 handle
= empathy_contact_get_handle (contact
);
147 tp_cli_channel_interface_group_call_remove_members (priv
->channel
, -1,
154 tp_chat_get_members (EmpathyContactList
*list
)
156 EmpathyTpChatPriv
*priv
= GET_PRIV (list
);
157 GList
*members
= NULL
;
159 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list
), NULL
);
162 members
= g_list_copy (priv
->members
);
163 g_list_foreach (members
, (GFunc
) g_object_ref
, NULL
);
165 members
= g_list_prepend (members
, g_object_ref (priv
->user
));
166 members
= g_list_prepend (members
, g_object_ref (priv
->remote_contact
));
172 static EmpathyContactMonitor
*
173 tp_chat_get_monitor (EmpathyContactList
*list
)
175 EmpathyTpChatPriv
*priv
;
177 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list
), NULL
);
179 priv
= GET_PRIV (list
);
181 if (priv
->contact_monitor
== NULL
) {
182 priv
->contact_monitor
= empathy_contact_monitor_new_for_iface (list
);
185 return priv
->contact_monitor
;
189 tp_chat_emit_queued_messages (EmpathyTpChat
*chat
)
191 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
192 EmpathyMessage
*message
;
194 /* Check if we can now emit some queued messages */
195 while ((message
= g_queue_peek_head (priv
->messages_queue
)) != NULL
) {
196 if (empathy_message_get_sender (message
) == NULL
) {
200 DEBUG ("Queued message ready");
201 g_queue_pop_head (priv
->messages_queue
);
202 g_queue_push_tail (priv
->pending_messages_queue
, message
);
203 g_signal_emit (chat
, signals
[MESSAGE_RECEIVED
], 0, message
);
208 tp_chat_got_sender_cb (EmpathyTpContactFactory
*factory
,
209 EmpathyContact
*contact
,
214 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
217 DEBUG ("Error: %s", error
->message
);
218 /* Do not block the message queue, just drop this message */
219 g_queue_remove (priv
->messages_queue
, message
);
221 empathy_message_set_sender (message
, contact
);
224 tp_chat_emit_queued_messages (EMPATHY_TP_CHAT (chat
));
228 tp_chat_build_message (EmpathyTpChat
*chat
,
233 const gchar
*message_body
)
235 EmpathyTpChatPriv
*priv
;
236 EmpathyMessage
*message
;
238 priv
= GET_PRIV (chat
);
240 message
= empathy_message_new (message_body
);
241 empathy_message_set_tptype (message
, type
);
242 empathy_message_set_receiver (message
, priv
->user
);
243 empathy_message_set_timestamp (message
, timestamp
);
244 empathy_message_set_id (message
, id
);
245 g_queue_push_tail (priv
->messages_queue
, message
);
247 if (from_handle
== 0) {
248 empathy_message_set_sender (message
, priv
->user
);
249 tp_chat_emit_queued_messages (chat
);
251 empathy_tp_contact_factory_get_from_handle (priv
->factory
,
253 tp_chat_got_sender_cb
,
254 message
, NULL
, G_OBJECT (chat
));
259 tp_chat_received_cb (TpChannel
*channel
,
265 const gchar
*message_body
,
269 EmpathyTpChat
*chat
= EMPATHY_TP_CHAT (chat_
);
270 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
272 if (priv
->channel
== NULL
)
275 if (priv
->listing_pending_messages
) {
279 DEBUG ("Message received: %s", message_body
);
281 if (message_flags
& TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT
&&
282 !tp_strdiff (message_body
, "")) {
285 DEBUG ("Empty message with NonTextContent, ignoring and acking.");
287 ids
= g_array_sized_new (FALSE
, FALSE
, sizeof (guint
), 1);
288 g_array_append_val (ids
, message_id
);
289 acknowledge_messages (chat
, ids
);
290 g_array_free (ids
, TRUE
);
295 tp_chat_build_message (chat
,
304 tp_chat_sent_cb (TpChannel
*channel
,
307 const gchar
*message_body
,
311 EmpathyTpChat
*chat
= EMPATHY_TP_CHAT (chat_
);
312 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
314 if (priv
->channel
== NULL
)
317 DEBUG ("Message sent: %s", message_body
);
319 tp_chat_build_message (chat
,
328 tp_chat_send_error_cb (TpChannel
*channel
,
332 const gchar
*message_body
,
336 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
338 if (priv
->channel
== NULL
)
341 DEBUG ("Message sent error: %s (%d)", message_body
, error_code
);
343 tp_chat_build_message (EMPATHY_TP_CHAT (chat
),
352 tp_chat_send_cb (TpChannel
*proxy
,
357 EmpathyMessage
*message
= EMPATHY_MESSAGE (user_data
);
360 DEBUG ("Error: %s", error
->message
);
361 g_signal_emit (chat
, signals
[SEND_ERROR
], 0, message
,
362 TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN
);
368 TpChannelChatState state
;
372 tp_chat_state_changed_got_contact_cb (EmpathyTpContactFactory
*factory
,
373 EmpathyContact
*contact
,
378 TpChannelChatState state
;
381 DEBUG ("Error: %s", error
->message
);
385 state
= GPOINTER_TO_UINT (user_data
);
386 DEBUG ("Chat state changed for %s (%d): %d",
387 empathy_contact_get_name (contact
),
388 empathy_contact_get_handle (contact
), state
);
390 g_signal_emit (chat
, signals
[CHAT_STATE_CHANGED
], 0, contact
, state
);
394 tp_chat_state_changed_cb (TpChannel
*channel
,
396 TpChannelChatState state
,
400 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
402 empathy_tp_contact_factory_get_from_handle (priv
->factory
, handle
,
403 tp_chat_state_changed_got_contact_cb
, GUINT_TO_POINTER (state
),
408 tp_chat_list_pending_messages_cb (TpChannel
*channel
,
409 const GPtrArray
*messages_list
,
414 EmpathyTpChat
*chat
= EMPATHY_TP_CHAT (chat_
);
415 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
417 GArray
*empty_non_text_content_ids
= NULL
;
419 priv
->listing_pending_messages
= FALSE
;
421 if (priv
->channel
== NULL
)
425 DEBUG ("Error listing pending messages: %s", error
->message
);
429 for (i
= 0; i
< messages_list
->len
; i
++) {
430 GValueArray
*message_struct
;
431 const gchar
*message_body
;
438 message_struct
= g_ptr_array_index (messages_list
, i
);
440 message_id
= g_value_get_uint (g_value_array_get_nth (message_struct
, 0));
441 timestamp
= g_value_get_uint (g_value_array_get_nth (message_struct
, 1));
442 from_handle
= g_value_get_uint (g_value_array_get_nth (message_struct
, 2));
443 message_type
= g_value_get_uint (g_value_array_get_nth (message_struct
, 3));
444 message_flags
= g_value_get_uint (g_value_array_get_nth (message_struct
, 4));
445 message_body
= g_value_get_string (g_value_array_get_nth (message_struct
, 5));
447 DEBUG ("Message pending: %s", message_body
);
449 if (message_flags
& TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT
&&
450 !tp_strdiff (message_body
, "")) {
451 DEBUG ("Empty message with NonTextContent, ignoring and acking.");
453 if (empty_non_text_content_ids
== NULL
) {
454 empty_non_text_content_ids
= g_array_new (FALSE
, FALSE
, sizeof (guint
));
457 g_array_append_val (empty_non_text_content_ids
, message_id
);
461 tp_chat_build_message (chat
,
469 if (empty_non_text_content_ids
!= NULL
) {
470 acknowledge_messages (chat
, empty_non_text_content_ids
);
471 g_array_free (empty_non_text_content_ids
, TRUE
);
476 tp_chat_property_flags_changed_cb (TpProxy
*proxy
,
477 const GPtrArray
*properties
,
481 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
484 if (priv
->channel
== NULL
)
487 if (!priv
->had_properties_list
|| !properties
) {
491 for (i
= 0; i
< properties
->len
; i
++) {
492 GValueArray
*prop_struct
;
493 TpChatProperty
*property
;
497 prop_struct
= g_ptr_array_index (properties
, i
);
498 id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
499 flags
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 1));
501 for (j
= 0; j
< priv
->properties
->len
; j
++) {
502 property
= g_ptr_array_index (priv
->properties
, j
);
503 if (property
->id
== id
) {
504 property
->flags
= flags
;
505 DEBUG ("property %s flags changed: %d",
506 property
->name
, property
->flags
);
514 tp_chat_properties_changed_cb (TpProxy
*proxy
,
515 const GPtrArray
*properties
,
519 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
522 if (priv
->channel
== NULL
)
525 if (!priv
->had_properties_list
|| !properties
) {
529 for (i
= 0; i
< properties
->len
; i
++) {
530 GValueArray
*prop_struct
;
531 TpChatProperty
*property
;
535 prop_struct
= g_ptr_array_index (properties
, i
);
536 id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
537 src_value
= g_value_get_boxed (g_value_array_get_nth (prop_struct
, 1));
539 for (j
= 0; j
< priv
->properties
->len
; j
++) {
540 property
= g_ptr_array_index (priv
->properties
, j
);
541 if (property
->id
== id
) {
542 if (property
->value
) {
543 g_value_copy (src_value
, property
->value
);
545 property
->value
= tp_g_value_slice_dup (src_value
);
548 DEBUG ("property %s changed", property
->name
);
549 g_signal_emit (chat
, signals
[PROPERTY_CHANGED
], 0,
550 property
->name
, property
->value
);
558 tp_chat_get_properties_cb (TpProxy
*proxy
,
559 const GPtrArray
*properties
,
565 DEBUG ("Error getting properties: %s", error
->message
);
569 tp_chat_properties_changed_cb (proxy
, properties
, user_data
, chat
);
573 tp_chat_list_properties_cb (TpProxy
*proxy
,
574 const GPtrArray
*properties
,
579 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
583 if (priv
->channel
== NULL
)
586 priv
->had_properties_list
= TRUE
;
589 DEBUG ("Error listing properties: %s", error
->message
);
593 ids
= g_array_sized_new (FALSE
, FALSE
, sizeof (guint
), properties
->len
);
594 priv
->properties
= g_ptr_array_sized_new (properties
->len
);
595 for (i
= 0; i
< properties
->len
; i
++) {
596 GValueArray
*prop_struct
;
597 TpChatProperty
*property
;
599 prop_struct
= g_ptr_array_index (properties
, i
);
600 property
= g_slice_new0 (TpChatProperty
);
601 property
->id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
602 property
->name
= g_value_dup_string (g_value_array_get_nth (prop_struct
, 1));
603 property
->flags
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 3));
605 DEBUG ("Adding property name=%s id=%d flags=%d",
606 property
->name
, property
->id
, property
->flags
);
607 g_ptr_array_add (priv
->properties
, property
);
608 if (property
->flags
& TP_PROPERTY_FLAG_READ
) {
609 g_array_append_val (ids
, property
->id
);
613 tp_cli_properties_interface_call_get_properties (proxy
, -1,
615 tp_chat_get_properties_cb
,
619 g_array_free (ids
, TRUE
);
623 empathy_tp_chat_set_property (EmpathyTpChat
*chat
,
627 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
628 TpChatProperty
*property
;
631 for (i
= 0; i
< priv
->properties
->len
; i
++) {
632 property
= g_ptr_array_index (priv
->properties
, i
);
633 if (!tp_strdiff (property
->name
, name
)) {
634 GPtrArray
*properties
;
637 GValue dest_value
= {0, };
639 if (!(property
->flags
& TP_PROPERTY_FLAG_WRITE
)) {
643 g_value_init (&id
, G_TYPE_UINT
);
644 g_value_init (&dest_value
, G_TYPE_VALUE
);
645 g_value_set_uint (&id
, property
->id
);
646 g_value_set_boxed (&dest_value
, value
);
648 prop
= g_value_array_new (2);
649 g_value_array_append (prop
, &id
);
650 g_value_array_append (prop
, &dest_value
);
652 properties
= g_ptr_array_sized_new (1);
653 g_ptr_array_add (properties
, prop
);
655 DEBUG ("Set property %s", name
);
656 tp_cli_properties_interface_call_set_properties (priv
->channel
, -1,
658 (tp_cli_properties_interface_callback_for_set_properties
)
660 "Seting property", NULL
,
663 g_ptr_array_free (properties
, TRUE
);
664 g_value_array_free (prop
);
672 tp_chat_dispose (GObject
*object
)
674 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (object
);
675 EmpathyTpChatPriv
*priv
= GET_PRIV (self
);
677 if (priv
->dispose_has_run
)
680 priv
->dispose_has_run
= TRUE
;
682 if (priv
->channel
!= NULL
) {
683 g_signal_handlers_disconnect_by_func (priv
->channel
,
684 tp_chat_invalidated_cb
, self
);
685 g_object_unref (priv
->channel
);
687 priv
->channel
= NULL
;
689 if (priv
->remote_contact
!= NULL
)
690 g_object_unref (priv
->remote_contact
);
691 priv
->remote_contact
= NULL
;
693 if (priv
->factory
!= NULL
)
694 g_object_unref (priv
->factory
);
695 priv
->factory
= NULL
;
697 if (priv
->user
!= NULL
)
698 g_object_unref (priv
->user
);
701 if (priv
->contact_monitor
)
702 g_object_unref (priv
->contact_monitor
);
703 priv
->contact_monitor
= NULL
;
705 g_queue_foreach (priv
->messages_queue
, (GFunc
) g_object_unref
, NULL
);
706 g_queue_clear (priv
->messages_queue
);
708 g_queue_foreach (priv
->pending_messages_queue
,
709 (GFunc
) g_object_unref
, NULL
);
710 g_queue_clear (priv
->pending_messages_queue
);
712 if (G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->dispose
)
713 G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->dispose (object
);
717 tp_chat_finalize (GObject
*object
)
719 EmpathyTpChatPriv
*priv
= GET_PRIV (object
);
722 DEBUG ("Finalize: %p", object
);
724 if (priv
->properties
) {
725 for (i
= 0; i
< priv
->properties
->len
; i
++) {
726 TpChatProperty
*property
;
728 property
= g_ptr_array_index (priv
->properties
, i
);
729 g_free (property
->name
);
730 if (property
->value
) {
731 tp_g_value_slice_free (property
->value
);
733 g_slice_free (TpChatProperty
, property
);
735 g_ptr_array_free (priv
->properties
, TRUE
);
738 g_queue_free (priv
->messages_queue
);
739 g_queue_free (priv
->pending_messages_queue
);
741 G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->finalize (object
);
745 tp_chat_check_if_ready (EmpathyTpChat
*chat
)
747 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
749 if (priv
->ready
|| priv
->user
== NULL
||
750 (priv
->members
== NULL
&& priv
->remote_contact
== NULL
)) {
756 tp_cli_channel_type_text_connect_to_received (priv
->channel
,
759 G_OBJECT (chat
), NULL
);
760 priv
->listing_pending_messages
= TRUE
;
761 tp_cli_channel_type_text_call_list_pending_messages (priv
->channel
, -1,
763 tp_chat_list_pending_messages_cb
,
767 tp_cli_channel_type_text_connect_to_sent (priv
->channel
,
770 G_OBJECT (chat
), NULL
);
771 tp_cli_channel_type_text_connect_to_send_error (priv
->channel
,
772 tp_chat_send_error_cb
,
774 G_OBJECT (chat
), NULL
);
775 tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv
->channel
,
776 tp_chat_state_changed_cb
,
778 G_OBJECT (chat
), NULL
);
779 tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv
->channel
,
780 tp_chat_state_changed_cb
,
782 G_OBJECT (chat
), NULL
);
784 g_object_notify (G_OBJECT (chat
), "ready");
788 tp_chat_update_remote_contact (EmpathyTpChat
*chat
)
790 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
791 EmpathyContact
*contact
= NULL
;
792 TpHandle self_handle
;
793 TpHandleType handle_type
;
796 /* If this is a named chatroom, never pretend it is a private chat */
797 tp_channel_get_handle (priv
->channel
, &handle_type
);
798 if (handle_type
== TP_HANDLE_TYPE_ROOM
) {
802 /* This is an MSN-like chat where anyone can join the chat at anytime.
803 * If there is only one non-self contact member, we are in a private
804 * chat and we set the "remote-contact" property to that contact. If
805 * there are more, set the "remote-contact" property to NULL and the
806 * UI will display a contact list. */
807 self_handle
= tp_channel_group_get_self_handle (priv
->channel
);
808 for (l
= priv
->members
; l
; l
= l
->next
) {
809 /* Skip self contact if member */
810 if (empathy_contact_get_handle (l
->data
) == self_handle
) {
814 /* We have more than one remote contact, break */
815 if (contact
!= NULL
) {
820 /* If we didn't find yet a remote contact, keep this one */
824 if (priv
->remote_contact
== contact
) {
828 DEBUG ("Changing remote contact from %p to %p",
829 priv
->remote_contact
, contact
);
831 if (priv
->remote_contact
) {
832 g_object_unref (priv
->remote_contact
);
835 priv
->remote_contact
= contact
? g_object_ref (contact
) : NULL
;
836 g_object_notify (G_OBJECT (chat
), "remote-contact");
840 tp_chat_got_added_contacts_cb (EmpathyTpContactFactory
*factory
,
842 EmpathyContact
* const * contacts
,
844 const TpHandle
*failed
,
849 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
851 const TpIntSet
*members
;
853 EmpathyContact
*contact
;
856 DEBUG ("Error: %s", error
->message
);
860 members
= tp_channel_group_get_members (priv
->channel
);
861 for (i
= 0; i
< n_contacts
; i
++) {
862 contact
= contacts
[i
];
863 handle
= empathy_contact_get_handle (contact
);
865 /* Make sure the contact is still member */
866 if (tp_intset_is_member (members
, handle
)) {
867 priv
->members
= g_list_prepend (priv
->members
,
868 g_object_ref (contact
));
869 g_signal_emit_by_name (chat
, "members-changed",
870 contact
, NULL
, 0, NULL
, TRUE
);
874 tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat
));
875 tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat
));
878 static EmpathyContact
*
879 chat_lookup_contact (EmpathyTpChat
*chat
,
883 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
886 for (l
= priv
->members
; l
; l
= l
->next
) {
887 EmpathyContact
*c
= l
->data
;
889 if (empathy_contact_get_handle (c
) == handle
) {
891 /* Caller takes the reference. */
892 priv
->members
= g_list_delete_link (
906 tp_chat_group_members_changed_cb (TpChannel
*self
,
910 GArray
*local_pending
,
911 GArray
*remote_pending
,
916 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
917 EmpathyContact
*contact
, *actor_contact
= NULL
;
921 actor_contact
= chat_lookup_contact (chat
, actor
, FALSE
);
922 if (actor_contact
== NULL
) {
923 /* TODO: handle this a tad more gracefully: perhaps the
924 * actor was a server op. We could use the contact-ids
925 * detail of MembersChangedDetailed.
927 DEBUG ("actor %u not a channel member", actor
);
931 /* Remove contacts that are not members anymore */
932 for (i
= 0; i
< removed
->len
; i
++) {
933 contact
= chat_lookup_contact (chat
,
934 g_array_index (removed
, TpHandle
, i
), TRUE
);
936 if (contact
!= NULL
) {
937 g_signal_emit_by_name (chat
, "members-changed", contact
,
938 actor_contact
, reason
, message
,
940 g_object_unref (contact
);
944 /* Request added contacts */
945 if (added
->len
> 0) {
946 empathy_tp_contact_factory_get_from_handles (priv
->factory
,
947 added
->len
, (TpHandle
*) added
->data
,
948 tp_chat_got_added_contacts_cb
, NULL
, NULL
,
952 tp_chat_update_remote_contact (chat
);
954 if (actor_contact
!= NULL
) {
955 g_object_unref (actor_contact
);
960 tp_chat_got_remote_contact_cb (EmpathyTpContactFactory
*factory
,
961 EmpathyContact
*contact
,
966 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
969 DEBUG ("Error: %s", error
->message
);
970 empathy_tp_chat_close (EMPATHY_TP_CHAT (chat
));
974 priv
->remote_contact
= g_object_ref (contact
);
975 g_object_notify (chat
, "remote-contact");
977 tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat
));
981 tp_chat_got_self_contact_cb (EmpathyTpContactFactory
*factory
,
982 EmpathyContact
*contact
,
987 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
990 DEBUG ("Error: %s", error
->message
);
991 empathy_tp_chat_close (EMPATHY_TP_CHAT (chat
));
995 priv
->user
= g_object_ref (contact
);
996 empathy_contact_set_is_user (priv
->user
, TRUE
);
997 tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat
));
1001 tp_chat_constructor (GType type
,
1003 GObjectConstructParam
*props
)
1006 EmpathyTpChatPriv
*priv
;
1007 TpConnection
*connection
;
1010 chat
= G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->constructor (type
, n_props
, props
);
1012 priv
= GET_PRIV (chat
);
1014 connection
= tp_channel_borrow_connection (priv
->channel
);
1015 priv
->factory
= empathy_tp_contact_factory_dup_singleton (connection
);
1016 g_signal_connect (priv
->channel
, "invalidated",
1017 G_CALLBACK (tp_chat_invalidated_cb
),
1020 if (tp_proxy_has_interface_by_id (priv
->channel
,
1021 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
)) {
1022 const TpIntSet
*members
;
1025 /* Get self contact from the group's self handle */
1026 handle
= tp_channel_group_get_self_handle (priv
->channel
);
1027 empathy_tp_contact_factory_get_from_handle (priv
->factory
,
1028 handle
, tp_chat_got_self_contact_cb
,
1031 /* Get initial member contacts */
1032 members
= tp_channel_group_get_members (priv
->channel
);
1033 handles
= tp_intset_to_array (members
);
1034 empathy_tp_contact_factory_get_from_handles (priv
->factory
,
1035 handles
->len
, (TpHandle
*) handles
->data
,
1036 tp_chat_got_added_contacts_cb
, NULL
, NULL
, chat
);
1038 g_signal_connect (priv
->channel
, "group-members-changed",
1039 G_CALLBACK (tp_chat_group_members_changed_cb
), chat
);
1041 /* Get the self contact from the connection's self handle */
1042 handle
= tp_connection_get_self_handle (connection
);
1043 empathy_tp_contact_factory_get_from_handle (priv
->factory
,
1044 handle
, tp_chat_got_self_contact_cb
,
1047 /* Get the remote contact */
1048 handle
= tp_channel_get_handle (priv
->channel
, NULL
);
1049 empathy_tp_contact_factory_get_from_handle (priv
->factory
,
1050 handle
, tp_chat_got_remote_contact_cb
,
1054 if (tp_proxy_has_interface_by_id (priv
->channel
,
1055 TP_IFACE_QUARK_PROPERTIES_INTERFACE
)) {
1056 tp_cli_properties_interface_call_list_properties (priv
->channel
, -1,
1057 tp_chat_list_properties_cb
,
1060 tp_cli_properties_interface_connect_to_properties_changed (priv
->channel
,
1061 tp_chat_properties_changed_cb
,
1063 G_OBJECT (chat
), NULL
);
1064 tp_cli_properties_interface_connect_to_property_flags_changed (priv
->channel
,
1065 tp_chat_property_flags_changed_cb
,
1067 G_OBJECT (chat
), NULL
);
1074 tp_chat_get_property (GObject
*object
,
1079 EmpathyTpChatPriv
*priv
= GET_PRIV (object
);
1083 g_value_set_object (value
, priv
->channel
);
1085 case PROP_REMOTE_CONTACT
:
1086 g_value_set_object (value
, priv
->remote_contact
);
1089 g_value_set_boolean (value
, priv
->ready
);
1092 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
1098 tp_chat_set_property (GObject
*object
,
1100 const GValue
*value
,
1103 EmpathyTpChatPriv
*priv
= GET_PRIV (object
);
1107 priv
->channel
= g_value_dup_object (value
);
1110 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
1116 empathy_tp_chat_class_init (EmpathyTpChatClass
*klass
)
1118 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
1120 object_class
->dispose
= tp_chat_dispose
;
1121 object_class
->finalize
= tp_chat_finalize
;
1122 object_class
->constructor
= tp_chat_constructor
;
1123 object_class
->get_property
= tp_chat_get_property
;
1124 object_class
->set_property
= tp_chat_set_property
;
1126 g_object_class_install_property (object_class
,
1128 g_param_spec_object ("channel",
1129 "telepathy channel",
1130 "The text channel for the chat",
1133 G_PARAM_CONSTRUCT_ONLY
));
1135 g_object_class_install_property (object_class
,
1136 PROP_REMOTE_CONTACT
,
1137 g_param_spec_object ("remote-contact",
1138 "The remote contact",
1139 "The remote contact if there is no group iface on the channel",
1140 EMPATHY_TYPE_CONTACT
,
1143 g_object_class_install_property (object_class
,
1145 g_param_spec_boolean ("ready",
1146 "Is the object ready",
1147 "This object can't be used until this becomes true",
1152 signals
[MESSAGE_RECEIVED
] =
1153 g_signal_new ("message-received",
1154 G_TYPE_FROM_CLASS (klass
),
1158 g_cclosure_marshal_VOID__OBJECT
,
1160 1, EMPATHY_TYPE_MESSAGE
);
1162 signals
[SEND_ERROR
] =
1163 g_signal_new ("send-error",
1164 G_TYPE_FROM_CLASS (klass
),
1168 _empathy_marshal_VOID__OBJECT_UINT
,
1170 2, EMPATHY_TYPE_MESSAGE
, G_TYPE_UINT
);
1172 signals
[CHAT_STATE_CHANGED
] =
1173 g_signal_new ("chat-state-changed",
1174 G_TYPE_FROM_CLASS (klass
),
1178 _empathy_marshal_VOID__OBJECT_UINT
,
1180 2, EMPATHY_TYPE_CONTACT
, G_TYPE_UINT
);
1182 signals
[PROPERTY_CHANGED
] =
1183 g_signal_new ("property-changed",
1184 G_TYPE_FROM_CLASS (klass
),
1188 _empathy_marshal_VOID__STRING_BOXED
,
1190 2, G_TYPE_STRING
, G_TYPE_VALUE
);
1193 g_signal_new ("destroy",
1194 G_TYPE_FROM_CLASS (klass
),
1198 g_cclosure_marshal_VOID__VOID
,
1202 g_type_class_add_private (object_class
, sizeof (EmpathyTpChatPriv
));
1206 empathy_tp_chat_init (EmpathyTpChat
*chat
)
1208 EmpathyTpChatPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (chat
,
1209 EMPATHY_TYPE_TP_CHAT
, EmpathyTpChatPriv
);
1212 priv
->contact_monitor
= NULL
;
1213 priv
->messages_queue
= g_queue_new ();
1214 priv
->pending_messages_queue
= g_queue_new ();
1218 tp_chat_iface_init (EmpathyContactListIface
*iface
)
1220 iface
->add
= tp_chat_add
;
1221 iface
->remove
= tp_chat_remove
;
1222 iface
->get_members
= tp_chat_get_members
;
1223 iface
->get_monitor
= tp_chat_get_monitor
;
1227 empathy_tp_chat_new (TpChannel
*channel
)
1229 return g_object_new (EMPATHY_TYPE_TP_CHAT
,
1235 empathy_tp_chat_close (EmpathyTpChat
*chat
) {
1236 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1238 /* If there are still messages left, it'll come back..
1239 * We loose the ordering of sent messages though */
1240 tp_cli_channel_call_close (priv
->channel
, -1, tp_chat_async_cb
,
1241 "closing channel", NULL
, NULL
);
1245 empathy_tp_chat_get_id (EmpathyTpChat
*chat
)
1247 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1249 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), NULL
);
1251 return tp_channel_get_identifier (priv
->channel
);
1255 empathy_tp_chat_get_remote_contact (EmpathyTpChat
*chat
)
1257 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1259 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), NULL
);
1260 g_return_val_if_fail (priv
->ready
, NULL
);
1262 return priv
->remote_contact
;
1266 empathy_tp_chat_get_channel (EmpathyTpChat
*chat
)
1268 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1270 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), NULL
);
1272 return priv
->channel
;
1276 empathy_tp_chat_get_connection (EmpathyTpChat
*chat
)
1278 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1280 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), NULL
);
1282 return tp_channel_borrow_connection (priv
->channel
);
1286 empathy_tp_chat_is_ready (EmpathyTpChat
*chat
)
1288 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1290 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), FALSE
);
1296 empathy_tp_chat_send (EmpathyTpChat
*chat
,
1297 EmpathyMessage
*message
)
1299 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1300 const gchar
*message_body
;
1301 TpChannelTextMessageType message_type
;
1303 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat
));
1304 g_return_if_fail (EMPATHY_IS_MESSAGE (message
));
1305 g_return_if_fail (priv
->ready
);
1307 message_body
= empathy_message_get_body (message
);
1308 message_type
= empathy_message_get_tptype (message
);
1310 DEBUG ("Sending message: %s", message_body
);
1311 tp_cli_channel_type_text_call_send (priv
->channel
, -1,
1315 g_object_ref (message
),
1316 (GDestroyNotify
) g_object_unref
,
1321 empathy_tp_chat_set_state (EmpathyTpChat
*chat
,
1322 TpChannelChatState state
)
1324 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1326 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat
));
1327 g_return_if_fail (priv
->ready
);
1329 DEBUG ("Set state: %d", state
);
1330 tp_cli_channel_interface_chat_state_call_set_chat_state (priv
->channel
, -1,
1333 "setting chat state",
1340 empathy_tp_chat_get_pending_messages (EmpathyTpChat
*chat
)
1342 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1344 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), NULL
);
1345 g_return_val_if_fail (priv
->ready
, NULL
);
1347 return priv
->pending_messages_queue
->head
;
1351 acknowledge_messages (EmpathyTpChat
*chat
, GArray
*ids
) {
1352 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1354 tp_cli_channel_type_text_call_acknowledge_pending_messages (
1355 priv
->channel
, -1, ids
, tp_chat_async_cb
,
1356 "acknowledging received message", NULL
, G_OBJECT (chat
));
1360 empathy_tp_chat_acknowledge_message (EmpathyTpChat
*chat
,
1361 EmpathyMessage
*message
) {
1362 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1363 GArray
*message_ids
;
1367 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat
));
1368 g_return_if_fail (priv
->ready
);
1370 if (empathy_message_get_sender (message
) == priv
->user
)
1373 message_ids
= g_array_sized_new (FALSE
, FALSE
, sizeof (guint
), 1);
1375 id
= empathy_message_get_id (message
);
1376 g_array_append_val (message_ids
, id
);
1377 acknowledge_messages (chat
, message_ids
);
1378 g_array_free (message_ids
, TRUE
);
1381 m
= g_queue_find (priv
->pending_messages_queue
, message
);
1382 g_assert (m
!= NULL
);
1383 g_queue_delete_link (priv
->pending_messages_queue
, m
);
1384 g_object_unref (message
);
1388 empathy_tp_chat_acknowledge_messages (EmpathyTpChat
*chat
,
1389 const GList
*messages
) {
1390 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1391 /* Copy messages as the messges list (probably is) our own */
1392 GList
*msgs
= g_list_copy ((GList
*) messages
);
1395 GArray
*message_ids
;
1397 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat
));
1398 g_return_if_fail (priv
->ready
);
1400 length
= g_list_length ((GList
*) messages
);
1405 message_ids
= g_array_sized_new (FALSE
, FALSE
, sizeof (guint
), length
);
1407 for (l
= msgs
; l
!= NULL
; l
= g_list_next (l
)) {
1410 EmpathyMessage
*message
= EMPATHY_MESSAGE (l
->data
);
1412 m
= g_queue_find (priv
->pending_messages_queue
, message
);
1413 g_assert (m
!= NULL
);
1414 g_queue_delete_link (priv
->pending_messages_queue
, m
);
1416 if (empathy_message_get_sender (message
) != priv
->user
) {
1417 guint id
= empathy_message_get_id (message
);
1418 g_array_append_val (message_ids
, id
);
1420 g_object_unref (message
);
1423 if (message_ids
->len
> 0)
1424 acknowledge_messages (chat
, message_ids
);
1426 g_array_free (message_ids
, TRUE
);