X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy%2Fempathy-tp-chat.c;h=b9b731384f5fa7be91dd3e6d8fec7e18feac003d;hp=50e05fc60a9f08e70ac7124989ae0f19043ef845;hb=20075065ac090531ca887c900bf8b0cb367a0336;hpb=9335e9e00a694a5614298fbf5fc8424640516e93 diff --git a/libempathy/empathy-tp-chat.c b/libempathy/empathy-tp-chat.c index 50e05fc6..b9b73138 100644 --- a/libempathy/empathy-tp-chat.c +++ b/libempathy/empathy-tp-chat.c @@ -28,35 +28,34 @@ #include #include "empathy-tp-chat.h" -#include "empathy-contact-factory.h" +#include "empathy-tp-contact-factory.h" +#include "empathy-contact-monitor.h" #include "empathy-contact-list.h" #include "empathy-marshal.h" -#include "empathy-debug.h" #include "empathy-time.h" #include "empathy-utils.h" -#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ - EMPATHY_TYPE_TP_CHAT, EmpathyTpChatPriv)) - -#define DEBUG_DOMAIN "TpChat" +#define DEBUG_FLAG EMPATHY_DEBUG_TP | EMPATHY_DEBUG_CHAT +#include "empathy-debug.h" -struct _EmpathyTpChatPriv { - EmpathyContactFactory *factory; +#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpChat) +typedef struct { + gboolean dispose_has_run; + EmpathyTpContactFactory *factory; + EmpathyContactMonitor *contact_monitor; EmpathyContact *user; EmpathyContact *remote_contact; - EmpathyTpGroup *group; - McAccount *account; + GList *members; TpChannel *channel; - gchar *id; - MissionControl *mc; - gboolean acknowledge; - TpChan *tp_chan; - gboolean had_pending_messages; - GSList *message_queue; + gboolean listing_pending_messages; + /* Queue of messages not signalled yet */ + GQueue *messages_queue; + /* Queue of messages signalled but not acked yet */ + GQueue *pending_messages_queue; gboolean had_properties_list; GPtrArray *properties; gboolean ready; -}; +} EmpathyTpChatPriv; typedef struct { gchar *name; @@ -65,16 +64,11 @@ typedef struct { GValue *value; } TpChatProperty; -static void empathy_tp_chat_class_init (EmpathyTpChatClass *klass); -static void empathy_tp_chat_init (EmpathyTpChat *chat); static void tp_chat_iface_init (EmpathyContactListIface *iface); enum { PROP_0, - PROP_ACCOUNT, - PROP_TP_CHAN, PROP_CHANNEL, - PROP_ACKNOWLEDGE, PROP_REMOTE_CONTACT, PROP_READY, }; @@ -94,6 +88,8 @@ G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat, empathy_tp_chat, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST, tp_chat_iface_init)); +static void acknowledge_messages (EmpathyTpChat *chat, GArray *ids); + static void tp_chat_invalidated_cb (TpProxy *proxy, guint domain, @@ -101,8 +97,13 @@ tp_chat_invalidated_cb (TpProxy *proxy, gchar *message, EmpathyTpChat *chat) { - empathy_debug (DEBUG_DOMAIN, "Channel invalidated: %s", message); + EmpathyTpChatPriv *priv = GET_PRIV (chat); + + DEBUG ("Channel invalidated: %s", message); g_signal_emit (chat, signals[DESTROY], 0); + + g_object_unref (priv->channel); + priv->channel = NULL; } static void @@ -112,62 +113,27 @@ tp_chat_async_cb (TpChannel *proxy, GObject *weak_object) { if (error) { - empathy_debug (DEBUG_DOMAIN, "Error %s: %s", - user_data, error->message); + DEBUG ("Error %s: %s", (gchar*) user_data, error->message); } } -static void -tp_chat_member_added_cb (EmpathyTpGroup *group, - EmpathyContact *contact, - EmpathyContact *actor, - guint reason, - const gchar *message, - EmpathyTpChat *chat) -{ - g_signal_emit_by_name (chat, "members-changed", - contact, actor, reason, message, - TRUE); -} - -static void -tp_chat_member_removed_cb (EmpathyTpGroup *group, - EmpathyContact *contact, - EmpathyContact *actor, - guint reason, - const gchar *message, - EmpathyTpChat *chat) -{ - g_signal_emit_by_name (chat, "members-changed", - contact, actor, reason, message, - FALSE); -} -static void -tp_chat_local_pending_cb (EmpathyTpGroup *group, - EmpathyContact *contact, - EmpathyContact *actor, - guint reason, - const gchar *message, - EmpathyTpChat *chat) -{ - g_signal_emit_by_name (chat, "pendings-changed", - contact, actor, reason, message, - TRUE); -} - static void tp_chat_add (EmpathyContactList *list, EmpathyContact *contact, const gchar *message) { EmpathyTpChatPriv *priv = GET_PRIV (list); + TpHandle handle; + GArray handles = {(gchar *) &handle, 1}; g_return_if_fail (EMPATHY_IS_TP_CHAT (list)); g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - if (priv->group) { - empathy_tp_group_add_member (priv->group, contact, message); - } + handle = empathy_contact_get_handle (contact); + tp_cli_channel_interface_group_call_add_members (priv->channel, -1, + &handles, NULL, + NULL, NULL, NULL, + NULL); } static void @@ -176,13 +142,17 @@ tp_chat_remove (EmpathyContactList *list, const gchar *message) { EmpathyTpChatPriv *priv = GET_PRIV (list); + TpHandle handle; + GArray handles = {(gchar *) &handle, 1}; g_return_if_fail (EMPATHY_IS_TP_CHAT (list)); g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - if (priv->group) { - empathy_tp_group_remove_member (priv->group, contact, message); - } + handle = empathy_contact_get_handle (contact); + tp_cli_channel_interface_group_call_remove_members (priv->channel, -1, + &handles, NULL, + NULL, NULL, NULL, + NULL); } static GList * @@ -193,8 +163,9 @@ tp_chat_get_members (EmpathyContactList *list) g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list), NULL); - if (priv->group) { - members = empathy_tp_group_get_members (priv->group); + if (priv->members) { + members = g_list_copy (priv->members); + g_list_foreach (members, (GFunc) g_object_ref, NULL); } else { members = g_list_prepend (members, g_object_ref (priv->user)); members = g_list_prepend (members, g_object_ref (priv->remote_contact)); @@ -203,112 +174,80 @@ tp_chat_get_members (EmpathyContactList *list) return members; } -static EmpathyMessage * -tp_chat_build_message (EmpathyTpChat *chat, - guint type, - guint timestamp, - guint from_handle, - const gchar *message_body) +static EmpathyContactMonitor * +tp_chat_get_monitor (EmpathyContactList *list) { EmpathyTpChatPriv *priv; - EmpathyMessage *message; - EmpathyContact *sender; - priv = GET_PRIV (chat); + g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list), NULL); - if (from_handle == 0) { - sender = g_object_ref (priv->user); - } else { - sender = empathy_contact_factory_get_from_handle (priv->factory, - priv->account, - from_handle); - } + priv = GET_PRIV (list); - message = empathy_message_new (message_body); - empathy_message_set_type (message, type); - empathy_message_set_sender (message, sender); - empathy_message_set_receiver (message, priv->user); - empathy_message_set_timestamp (message, timestamp); - - g_object_unref (sender); + if (priv->contact_monitor == NULL) { + priv->contact_monitor = empathy_contact_monitor_new_for_iface (list); + } - return message; + return priv->contact_monitor; } static void -tp_chat_sender_ready_notify_cb (EmpathyContact *contact, - GParamSpec *param_spec, - EmpathyTpChat *chat) +tp_chat_emit_queued_messages (EmpathyTpChat *chat) { - EmpathyTpChatPriv *priv = GET_PRIV (chat); - EmpathyMessage *message; - EmpathyContactReady ready; - EmpathyContact *sender; - gboolean removed = FALSE; - - /* Emit all messages queued until we find a message with not - * ready sender. When leaving this loop, sender is the first not ready - * contact queued and removed tells if at least one message got removed - * from the queue. */ - while (priv->message_queue) { - message = priv->message_queue->data; - sender = empathy_message_get_sender (message); - ready = empathy_contact_get_ready (sender); - - if (!(ready & EMPATHY_CONTACT_READY_NAME)) { + EmpathyTpChatPriv *priv = GET_PRIV (chat); + EmpathyMessage *message; + + /* Check if we can now emit some queued messages */ + while ((message = g_queue_peek_head (priv->messages_queue)) != NULL) { + if (empathy_message_get_sender (message) == NULL) { break; } - empathy_debug (DEBUG_DOMAIN, "Queued message ready"); + DEBUG ("Queued message ready"); + g_queue_pop_head (priv->messages_queue); + g_queue_push_tail (priv->pending_messages_queue, message); g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message); - priv->message_queue = g_slist_remove (priv->message_queue, - message); - g_object_unref (message); - removed = TRUE; } +} - if (removed) { - g_signal_handlers_disconnect_by_func (contact, - tp_chat_sender_ready_notify_cb, - chat); - - if (priv->message_queue) { - g_signal_connect (sender, "notify::ready", - G_CALLBACK (tp_chat_sender_ready_notify_cb), - chat); - } - } +static void +tp_chat_got_sender_cb (EmpathyTpContactFactory *factory, + GList *contacts, + gpointer message, + GObject *chat) +{ + empathy_message_set_sender (message, contacts->data); + tp_chat_emit_queued_messages (EMPATHY_TP_CHAT (chat)); } static void -tp_chat_emit_or_queue_message (EmpathyTpChat *chat, - EmpathyMessage *message) +tp_chat_build_message (EmpathyTpChat *chat, + guint id, + guint type, + guint timestamp, + guint from_handle, + const gchar *message_body) { - EmpathyTpChatPriv *priv = GET_PRIV (chat); - EmpathyContact *sender; - EmpathyContactReady ready; - - if (priv->message_queue != NULL) { - empathy_debug (DEBUG_DOMAIN, "Message queue not empty"); - priv->message_queue = g_slist_append (priv->message_queue, - g_object_ref (message)); - return; - } + EmpathyTpChatPriv *priv; + EmpathyMessage *message; - sender = empathy_message_get_sender (message); - ready = empathy_contact_get_ready (sender); - if (ready & EMPATHY_CONTACT_READY_NAME) { - empathy_debug (DEBUG_DOMAIN, "Message queue empty and sender ready"); - g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message); - return; - } + priv = GET_PRIV (chat); - empathy_debug (DEBUG_DOMAIN, "Sender not ready"); - priv->message_queue = g_slist_append (priv->message_queue, - g_object_ref (message)); - g_signal_connect (sender, "notify::ready", - G_CALLBACK (tp_chat_sender_ready_notify_cb), - chat); + message = empathy_message_new (message_body); + empathy_message_set_tptype (message, type); + empathy_message_set_receiver (message, priv->user); + empathy_message_set_timestamp (message, timestamp); + empathy_message_set_id (message, id); + g_queue_push_tail (priv->messages_queue, message); + + if (from_handle == 0) { + empathy_message_set_sender (message, priv->user); + tp_chat_emit_queued_messages (chat); + } else { + empathy_tp_contact_factory_get_from_handles (priv->factory, + 1, &from_handle, + tp_chat_got_sender_cb, + message, NULL, G_OBJECT (chat)); + } } static void @@ -320,40 +259,40 @@ tp_chat_received_cb (TpChannel *channel, guint message_flags, const gchar *message_body, gpointer user_data, - GObject *chat) + GObject *chat_) { + EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_); EmpathyTpChatPriv *priv = GET_PRIV (chat); - EmpathyMessage *message; - if (!priv->had_pending_messages) { + if (priv->channel == NULL) + return; + + if (priv->listing_pending_messages) { return; } - empathy_debug (DEBUG_DOMAIN, "Message received: %s", message_body); + DEBUG ("Message received: %s", message_body); - message = tp_chat_build_message (EMPATHY_TP_CHAT (chat), - message_type, - timestamp, - from_handle, - message_body); + if (message_flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT && + !tp_strdiff (message_body, "")) { + GArray *ids; - tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message); - g_object_unref (message); + DEBUG ("Empty message with NonTextContent, ignoring and acking."); + + ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); + g_array_append_val (ids, message_id); + acknowledge_messages (chat, ids); + g_array_free (ids, TRUE); - if (priv->acknowledge) { - GArray *message_ids; - - message_ids = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_append_val (message_ids, message_id); - tp_cli_channel_type_text_call_acknowledge_pending_messages (priv->channel, - -1, - message_ids, - tp_chat_async_cb, - "acknowledging received message", - NULL, - chat); - g_array_free (message_ids, TRUE); + return; } + + tp_chat_build_message (chat, + message_id, + message_type, + timestamp, + from_handle, + message_body); } static void @@ -362,20 +301,22 @@ tp_chat_sent_cb (TpChannel *channel, guint message_type, const gchar *message_body, gpointer user_data, - GObject *chat) + GObject *chat_) { - EmpathyMessage *message; + EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_); + EmpathyTpChatPriv *priv = GET_PRIV (chat); - empathy_debug (DEBUG_DOMAIN, "Message sent: %s", message_body); + if (priv->channel == NULL) + return; - message = tp_chat_build_message (EMPATHY_TP_CHAT (chat), - message_type, - timestamp, - 0, - message_body); + DEBUG ("Message sent: %s", message_body); - tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message); - g_object_unref (message); + tp_chat_build_message (chat, + 0, + message_type, + timestamp, + 0, + message_body); } static void @@ -387,41 +328,70 @@ tp_chat_send_error_cb (TpChannel *channel, gpointer user_data, GObject *chat) { - EmpathyMessage *message; + EmpathyTpChatPriv *priv = GET_PRIV (chat); - empathy_debug (DEBUG_DOMAIN, "Message sent error: %s (%d)", - message_body, error_code); + if (priv->channel == NULL) + return; - message = tp_chat_build_message (EMPATHY_TP_CHAT (chat), - message_type, - timestamp, - 0, - message_body); + DEBUG ("Message sent error: %s (%d)", message_body, error_code); - g_signal_emit (chat, signals[SEND_ERROR], 0, message, error_code); - g_object_unref (message); + tp_chat_build_message (EMPATHY_TP_CHAT (chat), + 0, + message_type, + timestamp, + 0, + message_body); +} + +static void +tp_chat_send_cb (TpChannel *proxy, + const GError *error, + gpointer user_data, + GObject *chat) +{ + EmpathyMessage *message = EMPATHY_MESSAGE (user_data); + + if (error) { + DEBUG ("Error: %s", error->message); + g_signal_emit (chat, signals[SEND_ERROR], 0, message, + TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN); + } +} + +typedef struct { + EmpathyTpChat *chat; + TpChannelChatState state; +} StateChangedData; + +static void +tp_chat_state_changed_got_contact_cb (EmpathyTpContactFactory *factory, + GList *contacts, + gpointer user_data, + GObject *chat) +{ + EmpathyContact *contact = contacts->data; + TpChannelChatState state; + + state = GPOINTER_TO_UINT (user_data); + DEBUG ("Chat state changed for %s (%d): %d", + empathy_contact_get_name (contact), + empathy_contact_get_handle (contact), state); + + g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state); } static void tp_chat_state_changed_cb (TpChannel *channel, - guint handle, - guint state, + TpHandle handle, + TpChannelChatState state, gpointer user_data, GObject *chat) { EmpathyTpChatPriv *priv = GET_PRIV (chat); - EmpathyContact *contact; - - contact = empathy_contact_factory_get_from_handle (priv->factory, - priv->account, - handle); - - empathy_debug (DEBUG_DOMAIN, "Chat state changed for %s (%d): %d", - empathy_contact_get_name (contact), - handle, state); - g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state); - g_object_unref (contact); + empathy_tp_contact_factory_get_from_handles (priv->factory, 1, &handle, + tp_chat_state_changed_got_contact_cb, GUINT_TO_POINTER (state), + NULL, chat); } static void @@ -429,27 +399,24 @@ tp_chat_list_pending_messages_cb (TpChannel *channel, const GPtrArray *messages_list, const GError *error, gpointer user_data, - GObject *chat) + GObject *chat_) { + EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_); EmpathyTpChatPriv *priv = GET_PRIV (chat); guint i; - GArray *message_ids = NULL; + GArray *empty_non_text_content_ids = NULL; - priv->had_pending_messages = TRUE; + priv->listing_pending_messages = FALSE; - if (error) { - empathy_debug (DEBUG_DOMAIN, "Error listing pending messages: %s", - error->message); + if (priv->channel == NULL) return; - } - if (priv->acknowledge) { - message_ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), - messages_list->len); + if (error) { + DEBUG ("Error listing pending messages: %s", error->message); + return; } for (i = 0; i < messages_list->len; i++) { - EmpathyMessage *message; GValueArray *message_struct; const gchar *message_body; guint message_id; @@ -467,31 +434,31 @@ tp_chat_list_pending_messages_cb (TpChannel *channel, message_flags = g_value_get_uint (g_value_array_get_nth (message_struct, 4)); message_body = g_value_get_string (g_value_array_get_nth (message_struct, 5)); - empathy_debug (DEBUG_DOMAIN, "Message pending: %s", message_body); + DEBUG ("Message pending: %s", message_body); - if (message_ids) { - g_array_append_val (message_ids, message_id); - } + if (message_flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT && + !tp_strdiff (message_body, "")) { + DEBUG ("Empty message with NonTextContent, ignoring and acking."); - message = tp_chat_build_message (EMPATHY_TP_CHAT (chat), - message_type, - timestamp, - from_handle, - message_body); + if (empty_non_text_content_ids == NULL) { + empty_non_text_content_ids = g_array_new (FALSE, FALSE, sizeof (guint)); + } - tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message); - g_object_unref (message); + g_array_append_val (empty_non_text_content_ids, message_id); + continue; + } + + tp_chat_build_message (chat, + message_id, + message_type, + timestamp, + from_handle, + message_body); } - if (message_ids) { - tp_cli_channel_type_text_call_acknowledge_pending_messages (priv->channel, - -1, - message_ids, - tp_chat_async_cb, - "acknowledging pending messages", - NULL, - chat); - g_array_free (message_ids, TRUE); + if (empty_non_text_content_ids != NULL) { + acknowledge_messages (chat, empty_non_text_content_ids); + g_array_free (empty_non_text_content_ids, TRUE); } } @@ -504,6 +471,9 @@ tp_chat_property_flags_changed_cb (TpProxy *proxy, EmpathyTpChatPriv *priv = GET_PRIV (chat); guint i, j; + if (priv->channel == NULL) + return; + if (!priv->had_properties_list || !properties) { return; } @@ -522,9 +492,8 @@ tp_chat_property_flags_changed_cb (TpProxy *proxy, property = g_ptr_array_index (priv->properties, j); if (property->id == id) { property->flags = flags; - empathy_debug (DEBUG_DOMAIN, - "property %s flags changed: %d", - property->name, property->flags); + DEBUG ("property %s flags changed: %d", + property->name, property->flags); break; } } @@ -540,6 +509,9 @@ tp_chat_properties_changed_cb (TpProxy *proxy, EmpathyTpChatPriv *priv = GET_PRIV (chat); guint i, j; + if (priv->channel == NULL) + return; + if (!priv->had_properties_list || !properties) { return; } @@ -563,8 +535,7 @@ tp_chat_properties_changed_cb (TpProxy *proxy, property->value = tp_g_value_slice_dup (src_value); } - empathy_debug (DEBUG_DOMAIN, "property %s changed", - property->name); + DEBUG ("property %s changed", property->name); g_signal_emit (chat, signals[PROPERTY_CHANGED], 0, property->name, property->value); break; @@ -581,8 +552,7 @@ tp_chat_get_properties_cb (TpProxy *proxy, GObject *chat) { if (error) { - empathy_debug (DEBUG_DOMAIN, "Error getting properties: %s", - error->message); + DEBUG ("Error getting properties: %s", error->message); return; } @@ -600,11 +570,13 @@ tp_chat_list_properties_cb (TpProxy *proxy, GArray *ids; guint i; + if (priv->channel == NULL) + return; + priv->had_properties_list = TRUE; if (error) { - empathy_debug (DEBUG_DOMAIN, "Error listing properties: %s", - error->message); + DEBUG ("Error listing properties: %s", error->message); return; } @@ -620,8 +592,8 @@ tp_chat_list_properties_cb (TpProxy *proxy, property->name = g_value_dup_string (g_value_array_get_nth (prop_struct, 1)); property->flags = g_value_get_uint (g_value_array_get_nth (prop_struct, 3)); - empathy_debug (DEBUG_DOMAIN, "Adding property name=%s id=%d flags=%d", - property->name, property->id, property->flags); + DEBUG ("Adding property name=%s id=%d flags=%d", + property->name, property->id, property->flags); g_ptr_array_add (priv->properties, property); if (property->flags & TP_PROPERTY_FLAG_READ) { g_array_append_val (ids, property->id); @@ -646,8 +618,6 @@ empathy_tp_chat_set_property (EmpathyTpChat *chat, TpChatProperty *property; guint i; - g_return_if_fail (priv->ready); - for (i = 0; i < priv->properties->len; i++) { property = g_ptr_array_index (priv->properties, i); if (!tp_strdiff (property->name, name)) { @@ -672,7 +642,7 @@ empathy_tp_chat_set_property (EmpathyTpChat *chat, properties = g_ptr_array_sized_new (1); g_ptr_array_add (properties, prop); - empathy_debug (DEBUG_DOMAIN, "Set property %s", name); + DEBUG ("Set property %s", name); tp_cli_properties_interface_call_set_properties (priv->channel, -1, properties, (tp_cli_properties_interface_callback_for_set_properties) @@ -689,51 +659,93 @@ empathy_tp_chat_set_property (EmpathyTpChat *chat, } static void -tp_chat_channel_ready_cb (EmpathyTpChat *chat) +tp_chat_dispose (GObject *object) { - EmpathyTpChatPriv *priv = GET_PRIV (chat); + EmpathyTpChat *self = EMPATHY_TP_CHAT (object); + EmpathyTpChatPriv *priv = GET_PRIV (self); - empathy_debug (DEBUG_DOMAIN, "Channel ready"); + if (priv->dispose_has_run) + return; - priv->ready = TRUE; - if (tp_proxy_has_interface_by_id (priv->channel, - TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP)) { - priv->group = empathy_tp_group_new (priv->account, priv->tp_chan); - - g_signal_connect (priv->group, "member-added", - G_CALLBACK (tp_chat_member_added_cb), - chat); - g_signal_connect (priv->group, "member-removed", - G_CALLBACK (tp_chat_member_removed_cb), - chat); - g_signal_connect (priv->group, "local-pending", - G_CALLBACK (tp_chat_local_pending_cb), - chat); - } else { - priv->remote_contact = empathy_contact_factory_get_from_handle (priv->factory, - priv->account, - priv->tp_chan->handle); - g_object_notify (G_OBJECT (chat), "remote-contact"); + priv->dispose_has_run = TRUE; + + if (priv->channel != NULL) { + g_signal_handlers_disconnect_by_func (priv->channel, + tp_chat_invalidated_cb, self); + g_object_unref (priv->channel); } - - if (tp_proxy_has_interface_by_id (priv->channel, - TP_IFACE_QUARK_PROPERTIES_INTERFACE)) { - tp_cli_properties_interface_call_list_properties (priv->channel, -1, - tp_chat_list_properties_cb, - NULL, NULL, - G_OBJECT (chat)); - tp_cli_properties_interface_connect_to_properties_changed (priv->channel, - tp_chat_properties_changed_cb, - NULL, NULL, - G_OBJECT (chat), NULL); - tp_cli_properties_interface_connect_to_property_flags_changed (priv->channel, - tp_chat_property_flags_changed_cb, - NULL, NULL, - G_OBJECT (chat), NULL); + priv->channel = NULL; + + if (priv->remote_contact != NULL) + g_object_unref (priv->remote_contact); + priv->remote_contact = NULL; + + if (priv->factory != NULL) + g_object_unref (priv->factory); + priv->factory = NULL; + + if (priv->user != NULL); + g_object_unref (priv->user); + priv->user = NULL; + + if (priv->contact_monitor) + g_object_unref (priv->contact_monitor); + priv->contact_monitor = NULL; + + g_queue_foreach (priv->messages_queue, (GFunc) g_object_unref, NULL); + g_queue_clear (priv->messages_queue); + + g_queue_foreach (priv->pending_messages_queue, + (GFunc) g_object_unref, NULL); + g_queue_clear (priv->pending_messages_queue); + + if (G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose) + G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose (object); +} + +static void +tp_chat_finalize (GObject *object) +{ + EmpathyTpChatPriv *priv = GET_PRIV (object); + guint i; + + DEBUG ("Finalize: %p", object); + + if (priv->properties) { + for (i = 0; i < priv->properties->len; i++) { + TpChatProperty *property; + + property = g_ptr_array_index (priv->properties, i); + g_free (property->name); + if (property->value) { + tp_g_value_slice_free (property->value); + } + g_slice_free (TpChatProperty, property); + } + g_ptr_array_free (priv->properties, TRUE); } + g_queue_free (priv->messages_queue); + g_queue_free (priv->pending_messages_queue); + + G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object); +} + +static void +tp_chat_check_if_ready (EmpathyTpChat *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); + + if (priv->ready || priv->user == NULL || + (priv->members == NULL && priv->remote_contact == NULL)) { + return; + } + + DEBUG ("Ready!"); + + priv->listing_pending_messages = TRUE; tp_cli_channel_type_text_call_list_pending_messages (priv->channel, -1, - priv->acknowledge, + FALSE, tp_chat_list_pending_messages_cb, NULL, NULL, G_OBJECT (chat)); @@ -758,60 +770,183 @@ tp_chat_channel_ready_cb (EmpathyTpChat *chat) tp_chat_state_changed_cb, NULL, NULL, G_OBJECT (chat), NULL); + priv->ready = TRUE; + g_object_notify (G_OBJECT (chat), "ready"); } static void -tp_chat_finalize (GObject *object) +tp_chat_update_remote_contact (EmpathyTpChat *chat) { - EmpathyTpChatPriv *priv = GET_PRIV (object); - guint i; + EmpathyTpChatPriv *priv = GET_PRIV (chat); + EmpathyContact *contact = NULL; + TpHandle self_handle; + TpHandleType handle_type; + GList *l; + + /* If this is a named chatroom, never pretend it is a private chat */ + tp_channel_get_handle (priv->channel, &handle_type); + if (handle_type == TP_HANDLE_TYPE_ROOM) { + return; + } - if (priv->acknowledge && priv->channel) { - empathy_debug (DEBUG_DOMAIN, "Closing channel..."); - tp_cli_channel_call_close (priv->channel, -1, - tp_chat_async_cb, - "closing channel", NULL, - NULL); + /* This is an MSN-like chat where anyone can join the chat at anytime. + * If there is only one non-self contact member, we are in a private + * chat and we set the "remote-contact" property to that contact. If + * there are more, set the "remote-contact" property to NULL and the + * UI will display a contact list. */ + self_handle = tp_channel_group_get_self_handle (priv->channel); + for (l = priv->members; l; l = l->next) { + /* Skip self contact if member */ + if (empathy_contact_get_handle (l->data) == self_handle) { + continue; + } + + /* We have more than one remote contact, break */ + if (contact != NULL) { + contact = NULL; + break; + } + + /* If we didn't find yet a remote contact, keep this one */ + contact = l->data; } - if (priv->channel) { - g_signal_handlers_disconnect_by_func (priv->channel, - tp_chat_invalidated_cb, - object); - g_object_unref (priv->channel); + if (priv->remote_contact == contact) { + return; } - if (priv->tp_chan) { - g_object_unref (priv->tp_chan); + + DEBUG ("Changing remote contact from %p to %p", + priv->remote_contact, contact); + + if (priv->remote_contact) { + g_object_unref (priv->remote_contact); } - if (priv->properties) { - for (i = 0; i < priv->properties->len; i++) { - TpChatProperty *property; + priv->remote_contact = contact ? g_object_ref (contact) : NULL; + g_object_notify (G_OBJECT (chat), "remote-contact"); +} - property = g_ptr_array_index (priv->properties, i); - g_free (property->name); - if (property->value) { - tp_g_value_slice_free (property->value); - } - g_slice_free (TpChatProperty, property); +static void +tp_chat_got_added_contacts_cb (EmpathyTpContactFactory *factory, + GList *contacts, + gpointer user_data, + GObject *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); + GList *l; + const TpIntSet *members; + TpHandle handle; + EmpathyContact *contact; + + members = tp_channel_group_get_members (priv->channel); + for (l = contacts; l; l = l->next) { + contact = l->data; + handle = empathy_contact_get_handle (contact); + + /* Make sure the contact is still member */ + if (tp_intset_is_member (members, handle)) { + priv->members = g_list_prepend (priv->members, + g_object_ref (contact)); + g_signal_emit_by_name (chat, "members-changed", + contact, NULL, 0, NULL, FALSE); } - g_ptr_array_free (priv->properties, TRUE); } - if (priv->remote_contact) { - g_object_unref (priv->remote_contact); + tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat)); + tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat)); +} + +static void +tp_chat_group_members_changed_cb (TpChannel *self, + gchar *message, + GArray *added, + GArray *removed, + GArray *local_pending, + GArray *remote_pending, + guint actor, + guint reason, + EmpathyTpChat *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); + EmpathyContact *contact; + TpHandle handle; + guint i; + GList *l; + + /* Remove contacts that are not members anymore */ + for (i = 0; i < removed->len; i++) { + for (l = priv->members; l; l = l->next) { + contact = l->data; + handle = empathy_contact_get_handle (contact); + if (handle == g_array_index (removed, TpHandle, i)) { + priv->members = g_list_delete_link (priv->members, l); + g_signal_emit_by_name (chat, "members-changed", + contact, actor, reason, + message, FALSE); + g_object_unref (contact); + break; + } + } } - if (priv->group) { - g_object_unref (priv->group); + + /* Request added contacts */ + if (added->len > 0) { + empathy_tp_contact_factory_get_from_handles (priv->factory, + added->len, (TpHandle*) added->data, + tp_chat_got_added_contacts_cb, NULL, NULL, + G_OBJECT (chat)); } - g_object_unref (priv->factory); - g_object_unref (priv->user); - g_object_unref (priv->account); - g_object_unref (priv->mc); - g_free (priv->id); + tp_chat_update_remote_contact (chat); +} - G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object); +static void +tp_chat_got_remote_contact_cb (EmpathyTpContactFactory *factory, + GList *contacts, + gpointer user_data, + GObject *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); + + priv->remote_contact = g_object_ref (contacts->data); + g_object_notify (chat, "remote-contact"); + + tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat)); +} + +static void +tp_chat_got_self_contact_cb (EmpathyTpContactFactory *factory, + GList *contacts, + gpointer user_data, + GObject *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); + + priv->user = g_object_ref (contacts->data); + empathy_contact_set_is_user (priv->user, TRUE); + tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat)); +} + +static void +tp_chat_get_self_handle_cb (TpConnection *connection, + TpHandle self_handle, + const GError *error, + gpointer user_data, + GObject *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); + + if (error) { + DEBUG ("Error: %s", error->message); + tp_cli_channel_call_close (priv->channel, -1, + NULL, NULL, NULL, NULL); + return; + } + + empathy_tp_contact_factory_get_from_handles (priv->factory, + 1, &self_handle, + tp_chat_got_self_contact_cb, + NULL, NULL, chat); } static GObject * @@ -821,26 +956,66 @@ tp_chat_constructor (GType type, { GObject *chat; EmpathyTpChatPriv *priv; - gboolean channel_ready; + TpConnection *connection; + TpHandle handle; chat = G_OBJECT_CLASS (empathy_tp_chat_parent_class)->constructor (type, n_props, props); priv = GET_PRIV (chat); - priv->factory = empathy_contact_factory_new (); - priv->user = empathy_contact_factory_get_user (priv->factory, priv->account); - priv->mc = empathy_mission_control_new (); + connection = tp_channel_borrow_connection (priv->channel); + priv->factory = empathy_tp_contact_factory_dup_singleton (connection); g_signal_connect (priv->channel, "invalidated", G_CALLBACK (tp_chat_invalidated_cb), chat); - g_object_get (priv->channel, "channel-ready", &channel_ready, NULL); - if (channel_ready) { - tp_chat_channel_ready_cb (EMPATHY_TP_CHAT (chat)); + if (tp_proxy_has_interface_by_id (priv->channel, + TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP)) { + const TpIntSet *members; + GArray *handles; + + /* Get self contact from the group's self handle */ + handle = tp_channel_group_get_self_handle (priv->channel); + empathy_tp_contact_factory_get_from_handles (priv->factory, + 1, &handle, + tp_chat_got_self_contact_cb, + NULL, NULL, chat); + + /* Get initial member contacts */ + members = tp_channel_group_get_members (priv->channel); + handles = tp_intset_to_array (members); + empathy_tp_contact_factory_get_from_handles (priv->factory, + handles->len, (TpHandle*) handles->data, + tp_chat_got_added_contacts_cb, NULL, NULL, chat); + + g_signal_connect_swapped (priv->channel, "group-members-changed", + G_CALLBACK (tp_chat_group_members_changed_cb), chat); } else { - g_signal_connect_swapped (priv->channel, "notify::channel-ready", - G_CALLBACK (tp_chat_channel_ready_cb), - chat); + /* Get the self contact from the connection's self handle */ + tp_cli_connection_call_get_self_handle (connection, -1, + tp_chat_get_self_handle_cb, NULL, NULL, chat); + + /* Get the remote contact */ + handle = tp_channel_get_handle (priv->channel, NULL); + empathy_tp_contact_factory_get_from_handles (priv->factory, + 1, &handle, tp_chat_got_remote_contact_cb, + NULL, NULL, chat); + } + + if (tp_proxy_has_interface_by_id (priv->channel, + TP_IFACE_QUARK_PROPERTIES_INTERFACE)) { + tp_cli_properties_interface_call_list_properties (priv->channel, -1, + tp_chat_list_properties_cb, + NULL, NULL, + G_OBJECT (chat)); + tp_cli_properties_interface_connect_to_properties_changed (priv->channel, + tp_chat_properties_changed_cb, + NULL, NULL, + G_OBJECT (chat), NULL); + tp_cli_properties_interface_connect_to_property_flags_changed (priv->channel, + tp_chat_property_flags_changed_cb, + NULL, NULL, + G_OBJECT (chat), NULL); } return chat; @@ -855,18 +1030,9 @@ tp_chat_get_property (GObject *object, EmpathyTpChatPriv *priv = GET_PRIV (object); switch (param_id) { - case PROP_ACCOUNT: - g_value_set_object (value, priv->account); - break; - case PROP_TP_CHAN: - g_value_set_object (value, priv->tp_chan); - break; case PROP_CHANNEL: g_value_set_object (value, priv->channel); break; - case PROP_ACKNOWLEDGE: - g_value_set_boolean (value, priv->acknowledge); - break; case PROP_REMOTE_CONTACT: g_value_set_object (value, priv->remote_contact); break; @@ -888,17 +1054,8 @@ tp_chat_set_property (GObject *object, EmpathyTpChatPriv *priv = GET_PRIV (object); switch (param_id) { - case PROP_ACCOUNT: - priv->account = g_object_ref (g_value_get_object (value)); - break; - case PROP_TP_CHAN: - priv->tp_chan = g_object_ref (g_value_get_object (value)); - break; case PROP_CHANNEL: - priv->channel = g_object_ref (g_value_get_object (value)); - break; - case PROP_ACKNOWLEDGE: - priv->acknowledge = g_value_get_boolean (value); + priv->channel = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -911,27 +1068,12 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->dispose = tp_chat_dispose; object_class->finalize = tp_chat_finalize; object_class->constructor = tp_chat_constructor; object_class->get_property = tp_chat_get_property; object_class->set_property = tp_chat_set_property; - g_object_class_install_property (object_class, - PROP_ACCOUNT, - g_param_spec_object ("account", - "channel Account", - "The account associated with the channel", - MC_TYPE_ACCOUNT, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY)); - g_object_class_install_property (object_class, - PROP_TP_CHAN, - g_param_spec_object ("tp-chan", - "telepathy channel", - "The text channel for the chat", - TELEPATHY_CHAN_TYPE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_CHANNEL, g_param_spec_object ("channel", @@ -940,14 +1082,6 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass) TP_TYPE_CHANNEL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - g_object_class_install_property (object_class, - PROP_ACKNOWLEDGE, - g_param_spec_boolean ("acknowledge", - "acknowledge messages", - "Wheter or not received messages should be acknowledged", - FALSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_REMOTE_CONTACT, @@ -956,6 +1090,7 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass) "The remote contact if there is no group iface on the channel", EMPATHY_TYPE_CONTACT, G_PARAM_READABLE)); + g_object_class_install_property (object_class, PROP_READY, g_param_spec_boolean ("ready", @@ -1021,6 +1156,13 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass) static void empathy_tp_chat_init (EmpathyTpChat *chat) { + EmpathyTpChatPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (chat, + EMPATHY_TYPE_TP_CHAT, EmpathyTpChatPriv); + + chat->priv = priv; + priv->contact_monitor = NULL; + priv->messages_queue = g_queue_new (); + priv->pending_messages_queue = g_queue_new (); } static void @@ -1029,124 +1171,108 @@ tp_chat_iface_init (EmpathyContactListIface *iface) iface->add = tp_chat_add; iface->remove = tp_chat_remove; iface->get_members = tp_chat_get_members; + iface->get_monitor = tp_chat_get_monitor; } EmpathyTpChat * -empathy_tp_chat_new (McAccount *account, - TpChan *tp_chan, - gboolean acknowledge) +empathy_tp_chat_new (TpChannel *channel) { - EmpathyTpChat *chat; - TpChannel *channel; - TpConnection *connection; - MissionControl *mc; - - mc = empathy_mission_control_new (); - connection = mission_control_get_tpconnection (mc, account, NULL); - channel = tp_chan_dup_channel (tp_chan, connection, NULL); - - chat = g_object_new (EMPATHY_TYPE_TP_CHAT, - "account", account, + return g_object_new (EMPATHY_TYPE_TP_CHAT, "channel", channel, - "tp-chan", tp_chan, - "acknowledge", acknowledge, NULL); +} + +void +empathy_tp_chat_close (EmpathyTpChat *chat) { + EmpathyTpChatPriv *priv = GET_PRIV (chat); - g_object_unref (channel); - g_object_unref (connection); - g_object_unref (mc); + /* If there are still messages left, it'll come back.. + We loose the ordering of sent messages though */ + g_signal_handlers_disconnect_by_func (priv->channel, + tp_chat_invalidated_cb, chat); - return chat; + tp_cli_channel_call_close (priv->channel, -1, tp_chat_async_cb, + "closing channel", NULL, NULL); + + g_object_unref (priv->channel); + priv->channel = NULL; + + g_signal_emit (chat, signals[DESTROY], 0); } -EmpathyTpChat * -empathy_tp_chat_new_with_contact (EmpathyContact *contact) +const gchar * +empathy_tp_chat_get_id (EmpathyTpChat *chat) { - EmpathyTpChat *chat; - MissionControl *mc; - McAccount *account; - TpConn *tp_conn; - TpChan *text_chan; - const gchar *bus_name; - guint handle; - - g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); - - mc = empathy_mission_control_new (); - account = empathy_contact_get_account (contact); - - if (mission_control_get_connection_status (mc, account, NULL) != 0) { - /* The account is not connected. */ - return NULL; - } + EmpathyTpChatPriv *priv = GET_PRIV (chat); - tp_conn = mission_control_get_connection (mc, account, NULL); - g_return_val_if_fail (tp_conn != NULL, NULL); - bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn)); - handle = empathy_contact_get_handle (contact); + g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL); - text_chan = tp_conn_new_channel (tp_get_bus (), - tp_conn, - bus_name, - TP_IFACE_CHANNEL_TYPE_TEXT, - TP_HANDLE_TYPE_CONTACT, - handle, - TRUE); + return tp_channel_get_identifier (priv->channel); +} - chat = empathy_tp_chat_new (account, text_chan, TRUE); +EmpathyContact * +empathy_tp_chat_get_remote_contact (EmpathyTpChat *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); - g_object_unref (tp_conn); - g_object_unref (text_chan); - g_object_unref (mc); + g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL); + g_return_val_if_fail (priv->ready, NULL); - return chat; + return priv->remote_contact; } -TpChan * +TpChannel * empathy_tp_chat_get_channel (EmpathyTpChat *chat) { - EmpathyTpChatPriv *priv; + EmpathyTpChatPriv *priv = GET_PRIV (chat); g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL); - priv = GET_PRIV (chat); - - return priv->tp_chan; + return priv->channel; } -McAccount * -empathy_tp_chat_get_account (EmpathyTpChat *chat) +TpConnection * +empathy_tp_chat_get_connection (EmpathyTpChat *chat) { - EmpathyTpChatPriv *priv; + EmpathyTpChatPriv *priv = GET_PRIV (chat); g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL); - priv = GET_PRIV (chat); + return tp_channel_borrow_connection (priv->channel); +} - return priv->account; +gboolean +empathy_tp_chat_is_ready (EmpathyTpChat *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); + + g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), FALSE); + + return priv->ready; } void empathy_tp_chat_send (EmpathyTpChat *chat, EmpathyMessage *message) { - EmpathyTpChatPriv *priv = GET_PRIV (chat); - const gchar *message_body; - EmpathyMessageType message_type; + EmpathyTpChatPriv *priv = GET_PRIV (chat); + const gchar *message_body; + TpChannelTextMessageType message_type; g_return_if_fail (EMPATHY_IS_TP_CHAT (chat)); g_return_if_fail (EMPATHY_IS_MESSAGE (message)); g_return_if_fail (priv->ready); message_body = empathy_message_get_body (message); - message_type = empathy_message_get_type (message); + message_type = empathy_message_get_tptype (message); - empathy_debug (DEBUG_DOMAIN, "Sending message: %s", message_body); + DEBUG ("Sending message: %s", message_body); tp_cli_channel_type_text_call_send (priv->channel, -1, message_type, message_body, - tp_chat_async_cb, - "sending message", NULL, + tp_chat_send_cb, + g_object_ref (message), + (GDestroyNotify) g_object_unref, G_OBJECT (chat)); } @@ -1159,7 +1285,7 @@ empathy_tp_chat_set_state (EmpathyTpChat *chat, g_return_if_fail (EMPATHY_IS_TP_CHAT (chat)); g_return_if_fail (priv->ready); - empathy_debug (DEBUG_DOMAIN, "Set state: %d", state); + DEBUG ("Set state: %d", state); tp_cli_channel_interface_chat_state_call_set_chat_state (priv->channel, -1, state, tp_chat_async_cb, @@ -1168,39 +1294,95 @@ empathy_tp_chat_set_state (EmpathyTpChat *chat, G_OBJECT (chat)); } -const gchar * -empathy_tp_chat_get_id (EmpathyTpChat *chat) + +const GList * +empathy_tp_chat_get_pending_messages (EmpathyTpChat *chat) { - EmpathyTpChatPriv *priv; + EmpathyTpChatPriv *priv = GET_PRIV (chat); g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL); + g_return_val_if_fail (priv->ready, NULL); - priv = GET_PRIV (chat); + return priv->pending_messages_queue->head; +} - if (!priv->id) { - priv->id = empathy_inspect_channel (priv->account, priv->tp_chan); - } +static void +acknowledge_messages (EmpathyTpChat *chat, GArray *ids) { + EmpathyTpChatPriv *priv = GET_PRIV (chat); - return priv->id; + tp_cli_channel_type_text_call_acknowledge_pending_messages ( + priv->channel, -1, ids, tp_chat_async_cb, + "acknowledging received message", NULL, G_OBJECT (chat)); } -EmpathyContact * -empathy_tp_chat_get_remote_contact (EmpathyTpChat *chat) -{ +void +empathy_tp_chat_acknowledge_message (EmpathyTpChat *chat, + EmpathyMessage *message) { EmpathyTpChatPriv *priv = GET_PRIV (chat); + GArray *message_ids; + GList *m; + guint id; - g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL); + g_return_if_fail (EMPATHY_IS_TP_CHAT (chat)); + g_return_if_fail (priv->ready); - return priv->remote_contact; + if (empathy_message_get_sender (message) == priv->user) + goto out; + + message_ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); + + id = empathy_message_get_id (message); + g_array_append_val (message_ids, id); + acknowledge_messages (chat, message_ids); + g_array_free (message_ids, TRUE); + +out: + m = g_queue_find (priv->pending_messages_queue, message); + g_assert (m != NULL); + g_queue_delete_link (priv->pending_messages_queue, m); + g_object_unref (message); } -gboolean -empathy_tp_chat_is_ready (EmpathyTpChat *chat) -{ +void +empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat, + const GList *messages) { EmpathyTpChatPriv *priv = GET_PRIV (chat); + /* Copy messages as the messges list (probably is) our own */ + GList *msgs = g_list_copy ((GList *) messages); + GList *l; + guint length; + GArray *message_ids; - g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), FALSE); + g_return_if_fail (EMPATHY_IS_TP_CHAT (chat)); + g_return_if_fail (priv->ready); - return priv->ready; + length = g_list_length ((GList *)messages); + + if (length == 0) + return; + + message_ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), length); + + for (l = msgs; l != NULL; l = g_list_next (l)) { + GList *m; + + EmpathyMessage *message = EMPATHY_MESSAGE (l->data); + + m = g_queue_find (priv->pending_messages_queue, message); + g_assert (m != NULL); + g_queue_delete_link (priv->pending_messages_queue, m); + + if (empathy_message_get_sender (message) != priv->user) { + guint id = empathy_message_get_id (message); + g_array_append_val (message_ids, id); + } + g_object_unref (message); + } + + if (message_ids->len > 0) + acknowledge_messages (chat, message_ids); + + g_array_free (message_ids, TRUE); + g_list_free (msgs); }