diff options
author | Xavier Claessens <xclaesse@src.gnome.org> | 2007-08-31 00:49:52 +0800 |
---|---|---|
committer | Xavier Claessens <xclaesse@src.gnome.org> | 2007-08-31 00:49:52 +0800 |
commit | 0b0bb0f621d8e1cda1508d9062a279459fc3b955 (patch) | |
tree | 03ad0e8b2180f5e3d6821fbab6315b33c7baf4d8 /libempathy | |
parent | 3a535643a694d0dd1bed8e0b7a2fd6fd21e6ee0c (diff) | |
download | gsoc2013-empathy-0b0bb0f621d8e1cda1508d9062a279459fc3b955.tar gsoc2013-empathy-0b0bb0f621d8e1cda1508d9062a279459fc3b955.tar.gz gsoc2013-empathy-0b0bb0f621d8e1cda1508d9062a279459fc3b955.tar.bz2 gsoc2013-empathy-0b0bb0f621d8e1cda1508d9062a279459fc3b955.tar.lz gsoc2013-empathy-0b0bb0f621d8e1cda1508d9062a279459fc3b955.tar.xz gsoc2013-empathy-0b0bb0f621d8e1cda1508d9062a279459fc3b955.tar.zst gsoc2013-empathy-0b0bb0f621d8e1cda1508d9062a279459fc3b955.zip |
Completely reworked ContactList API. Fixes bug #471611, bug #467280, bug #459540 and bug #462907.
svn path=/trunk/; revision=280
Diffstat (limited to 'libempathy')
-rw-r--r-- | libempathy/Makefile.am | 6 | ||||
-rw-r--r-- | libempathy/empathy-avatar.c | 2 | ||||
-rw-r--r-- | libempathy/empathy-avatar.h | 4 | ||||
-rw-r--r-- | libempathy/empathy-contact-factory.c | 1102 | ||||
-rw-r--r-- | libempathy/empathy-contact-factory.h | 72 | ||||
-rw-r--r-- | libempathy/empathy-contact-list.c | 147 | ||||
-rw-r--r-- | libempathy/empathy-contact-list.h | 79 | ||||
-rw-r--r-- | libempathy/empathy-contact-manager.c | 684 | ||||
-rw-r--r-- | libempathy/empathy-contact-manager.h | 17 | ||||
-rw-r--r-- | libempathy/empathy-contact.c | 367 | ||||
-rw-r--r-- | libempathy/empathy-contact.h | 89 | ||||
-rw-r--r-- | libempathy/empathy-log-manager.c | 18 | ||||
-rw-r--r-- | libempathy/empathy-marshal.list | 12 | ||||
-rw-r--r-- | libempathy/empathy-tp-chat.c | 39 | ||||
-rw-r--r-- | libempathy/empathy-tp-chatroom.c | 164 | ||||
-rw-r--r-- | libempathy/empathy-tp-contact-list.c | 2205 | ||||
-rw-r--r-- | libempathy/empathy-tp-contact-list.h | 17 | ||||
-rw-r--r-- | libempathy/empathy-tp-group.c | 864 | ||||
-rw-r--r-- | libempathy/empathy-tp-group.h | 71 |
19 files changed, 3048 insertions, 2911 deletions
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am index 39dfa9b62..c24a16f21 100644 --- a/libempathy/Makefile.am +++ b/libempathy/Makefile.am @@ -17,7 +17,6 @@ lib_LTLIBRARIES = libempathy.la libempathy_la_SOURCES = \ empathy-conf.c \ - empathy-contact.c \ empathy-avatar.c \ empathy-time.c \ empathy-presence.c \ @@ -26,8 +25,10 @@ libempathy_la_SOURCES = \ empathy-message.c \ empathy-chatroom-manager.c \ empathy-chatroom.c \ + empathy-contact.c \ empathy-contact-list.c \ empathy-contact-manager.c \ + empathy-contact-factory.c \ empathy-tp-group.c \ empathy-tp-contact-list.c \ empathy-tp-chat.c \ @@ -50,7 +51,6 @@ libempathy_la_LDFLAGS = \ libempathy_headers = \ empathy-conf.h \ - empathy-contact.h \ empathy-avatar.h \ empathy-time.h \ empathy-presence.h \ @@ -59,8 +59,10 @@ libempathy_headers = \ empathy-message.h \ empathy-chatroom-manager.h \ empathy-chatroom.h \ + empathy-contact.h \ empathy-contact-list.h \ empathy-contact-manager.h \ + empathy-contact-factory.h \ empathy-tp-group.h \ empathy-tp-contact-list.h \ empathy-tp-chat.h \ diff --git a/libempathy/empathy-avatar.c b/libempathy/empathy-avatar.c index e5a2a73a9..425f23f55 100644 --- a/libempathy/empathy-avatar.c +++ b/libempathy/empathy-avatar.c @@ -28,7 +28,7 @@ #define DEBUG_DOMAIN "Avatar" GType -empathy_avatar_get_gtype (void) +empathy_avatar_get_type (void) { static GType type_id = 0; diff --git a/libempathy/empathy-avatar.h b/libempathy/empathy-avatar.h index 73d69cf79..6b28e8e7a 100644 --- a/libempathy/empathy-avatar.h +++ b/libempathy/empathy-avatar.h @@ -25,7 +25,7 @@ G_BEGIN_DECLS -#define EMPATHY_TYPE_AVATAR (empathy_avatar_get_gtype ()) +#define EMPATHY_TYPE_AVATAR (empathy_avatar_get_type ()) typedef struct _EmpathyAvatar EmpathyAvatar; @@ -36,7 +36,7 @@ struct _EmpathyAvatar { guint refcount; }; -GType empathy_avatar_get_gtype (void) G_GNUC_CONST; +GType empathy_avatar_get_type (void) G_GNUC_CONST; EmpathyAvatar * empathy_avatar_new (guchar *avatar, gsize len, gchar *format); diff --git a/libempathy/empathy-contact-factory.c b/libempathy/empathy-contact-factory.c new file mode 100644 index 000000000..1c91a65b6 --- /dev/null +++ b/libempathy/empathy-contact-factory.c @@ -0,0 +1,1102 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#include <config.h> + +#include <string.h> + +#include <libtelepathy/tp-conn.h> +#include <libtelepathy/tp-conn-iface-aliasing-gen.h> +#include <libtelepathy/tp-conn-iface-presence-gen.h> +#include <libtelepathy/tp-conn-iface-avatars-gen.h> +#include <libmissioncontrol/mission-control.h> + +#include "empathy-contact-factory.h" +#include "empathy-utils.h" +#include "empathy-debug.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + EMPATHY_TYPE_CONTACT_FACTORY, EmpathyContactFactoryPriv)) + +#define DEBUG_DOMAIN "ContactFactory" + +struct _EmpathyContactFactoryPriv { + MissionControl *mc; + GHashTable *accounts; +}; + +typedef struct { + EmpathyContactFactory *factory; + McAccount *account; + guint nb_pending_calls; + + TpConn *tp_conn; + DBusGProxy *aliasing_iface; + DBusGProxy *avatars_iface; + DBusGProxy *presence_iface; + + GList *contacts; + guint self_handle; +} ContactFactoryAccountData; + +typedef struct { + ContactFactoryAccountData *account_data; + GList *contacts; +} RequestHandlesData; + +typedef struct { + ContactFactoryAccountData *account_data; + guint *handles; +} RequestAliasesData; + +typedef struct { + ContactFactoryAccountData *account_data; + EmpathyContact *contact; +} RequestAvatarData; + +static void empathy_contact_factory_class_init (EmpathyContactFactoryClass *klass); +static void empathy_contact_factory_init (EmpathyContactFactory *factory); + +G_DEFINE_TYPE (EmpathyContactFactory, empathy_contact_factory, G_TYPE_OBJECT); + +static gint +contact_factory_find_by_handle (gconstpointer a, + gconstpointer b) +{ + EmpathyContact *contact; + guint handle; + + contact = EMPATHY_CONTACT (a); + handle = GPOINTER_TO_UINT (b); + + return handle - empathy_contact_get_handle (contact); +} + +static EmpathyContact * +contact_factory_account_data_find_by_handle (ContactFactoryAccountData *account_data, + guint handle) +{ + GList *l; + + l = g_list_find_custom (account_data->contacts, + GUINT_TO_POINTER (handle), + contact_factory_find_by_handle); + + return l ? l->data : NULL; +} + +static gint +contact_factory_find_by_id (gconstpointer a, + gconstpointer b) +{ + EmpathyContact *contact; + const gchar *id = b; + + contact = EMPATHY_CONTACT (a); + + return strcmp (id, empathy_contact_get_id (contact)); +} + +static EmpathyContact * +contact_factory_account_data_find_by_id (ContactFactoryAccountData *account_data, + const gchar *id) +{ + GList *l; + + l = g_list_find_custom (account_data->contacts, + id, + contact_factory_find_by_id); + + return l ? l->data : NULL; +} + +static void contact_factory_account_data_free (gpointer data); + +static void +contact_factory_account_data_return_call (ContactFactoryAccountData *account_data) +{ + if (--account_data->nb_pending_calls == 0 && + account_data->contacts == NULL) { + contact_factory_account_data_free (account_data); + } +} + +static void +contact_factory_presences_table_foreach (const gchar *state_str, + GHashTable *presences_table, + EmpathyPresence **presence) +{ + McPresence state; + const GValue *message; + + state = empathy_presence_state_from_str (state_str); + if (state == MC_PRESENCE_UNSET) { + return; + } + + if (*presence) { + g_object_unref (*presence); + *presence = NULL; + } + + *presence = empathy_presence_new (); + empathy_presence_set_state (*presence, state); + + message = g_hash_table_lookup (presences_table, "message"); + if (message != NULL) { + empathy_presence_set_status (*presence, + g_value_get_string (message)); + } +} + +static void +contact_factory_parse_presence_foreach (guint handle, + GValueArray *presence_struct, + ContactFactoryAccountData *account_data) +{ + GHashTable *presences_table; + EmpathyContact *contact; + EmpathyPresence *presence = NULL; + + contact = contact_factory_account_data_find_by_handle (account_data, + handle); + if (!contact) { + return; + } + + presences_table = g_value_get_boxed (g_value_array_get_nth (presence_struct, 1)); + + g_hash_table_foreach (presences_table, + (GHFunc) contact_factory_presences_table_foreach, + &presence); + + empathy_debug (DEBUG_DOMAIN, "Changing presence for contact %s (%d) to %s (%d)", + empathy_contact_get_id (contact), + handle, + presence ? empathy_presence_get_status (presence) : "unset", + presence ? empathy_presence_get_state (presence) : MC_PRESENCE_UNSET); + + empathy_contact_set_presence (contact, presence); +} + +static void +contact_factory_get_presence_cb (DBusGProxy *proxy, + GHashTable *handle_table, + GError *error, + gpointer user_data) +{ + ContactFactoryAccountData *account_data = user_data; + + if (error) { + empathy_debug (DEBUG_DOMAIN, "Error requesting aliases: %s", + error->message); + goto OUT; + } + + g_hash_table_foreach (handle_table, + (GHFunc) contact_factory_parse_presence_foreach, + account_data); +OUT: + contact_factory_account_data_return_call (account_data); +} + +static void +contact_factory_presence_update_cb (DBusGProxy *proxy, + GHashTable *handle_table, + ContactFactoryAccountData *account_data) +{ + g_hash_table_foreach (handle_table, + (GHFunc) contact_factory_parse_presence_foreach, + account_data); +} + +static void +contact_factory_set_aliases_cb (DBusGProxy *proxy, + GError *error, + gpointer user_data) +{ + ContactFactoryAccountData *account_data = user_data; + + if (error) { + empathy_debug (DEBUG_DOMAIN, "Error setting alias: %s", + error->message); + } + + contact_factory_account_data_return_call (account_data); +} + +static void +contact_factory_request_aliases_cb (DBusGProxy *proxy, + gchar **contact_names, + GError *error, + gpointer user_data) +{ + RequestAliasesData *data = user_data; + guint i = 0; + gchar **name; + + if (error) { + empathy_debug (DEBUG_DOMAIN, "Error requesting aliases: %s", + error->message); + goto OUT; + } + + for (name = contact_names; *name; name++) { + EmpathyContact *contact; + + contact = contact_factory_account_data_find_by_handle (data->account_data, + data->handles[i]); + if (!contact) { + continue; + } + + empathy_debug (DEBUG_DOMAIN, "Renaming contact %s (%d) to %s (request cb)", + empathy_contact_get_id (contact), + data->handles[i], *name); + + empathy_contact_set_name (contact, *name); + + i++; + } + +OUT: + contact_factory_account_data_return_call (data->account_data); + g_free (data->handles); + g_slice_free (RequestAliasesData, data); +} + +static void +contact_factory_aliases_changed_cb (DBusGProxy *proxy, + GPtrArray *renamed_handlers, + gpointer user_data) +{ + ContactFactoryAccountData *account_data = user_data; + guint i; + + for (i = 0; renamed_handlers->len > i; i++) { + guint handle; + const gchar *alias; + GValueArray *renamed_struct; + EmpathyContact *contact; + + renamed_struct = g_ptr_array_index (renamed_handlers, i); + handle = g_value_get_uint(g_value_array_get_nth (renamed_struct, 0)); + alias = g_value_get_string(g_value_array_get_nth (renamed_struct, 1)); + contact = contact_factory_account_data_find_by_handle (account_data, handle); + + if (!contact) { + /* We don't know this contact, skip */ + continue; + } + + if (G_STR_EMPTY (alias)) { + alias = NULL; + } + + empathy_debug (DEBUG_DOMAIN, "Renaming contact %s (%d) to %s (changed cb)", + empathy_contact_get_id (contact), + handle, alias); + + empathy_contact_set_name (contact, alias); + } +} + +static void +contact_factory_request_avatars_cb (DBusGProxy *proxy, + GError *error, + gpointer user_data) +{ + ContactFactoryAccountData *account_data = user_data; + + if (error) { + empathy_debug (DEBUG_DOMAIN, "Error requesting avatars: %s", + error->message); + } + + contact_factory_account_data_return_call (account_data); +} + +static void +contact_factory_request_avatar_cb (DBusGProxy *proxy, + GArray *avatar_data, + gchar *mime_type, + GError *error, + gpointer user_data) +{ + RequestAvatarData *data = user_data; + EmpathyAvatar *avatar; + + if (error) { + empathy_debug (DEBUG_DOMAIN, "Error requesting avatar: %s", + error->message); + goto OUT; + } + + empathy_debug (DEBUG_DOMAIN, "Avatar received for %s (%d)", + empathy_contact_get_id (data->contact), + empathy_contact_get_handle (data->contact)); + + avatar = empathy_avatar_new (avatar_data->data, + avatar_data->len, + mime_type); + empathy_contact_set_avatar (data->contact, avatar); + empathy_avatar_unref (avatar); + +OUT: + g_object_unref (data->contact); + contact_factory_account_data_return_call (data->account_data); + g_slice_free (RequestAvatarData, data); +} + +static void +contact_factory_avatar_updated_cb (DBusGProxy *proxy, + guint handle, + gchar *new_token, + gpointer user_data) +{ + ContactFactoryAccountData *account_data = user_data; + RequestAvatarData *data; + EmpathyContact *contact; + + contact = contact_factory_account_data_find_by_handle (account_data, + handle); + if (!contact) { + return; + } + + account_data->nb_pending_calls++; + data = g_slice_new0 (RequestAvatarData); + data->account_data = account_data; + data->contact = g_object_ref (contact); + tp_conn_iface_avatars_request_avatar_async (account_data->avatars_iface, + handle, + contact_factory_request_avatar_cb, + data); +} + +static void +contact_factory_avatar_retrieved_cb (DBusGProxy *proxy, + guint handle, + gchar *token, + GArray *avatar_data, + gchar *mime_type, + gpointer user_data) +{ + ContactFactoryAccountData *account_data = user_data; + EmpathyContact *contact; + EmpathyAvatar *avatar; + + contact = contact_factory_account_data_find_by_handle (account_data, + handle); + if (!contact) { + return; + } + + avatar = empathy_avatar_new (avatar_data->data, + avatar_data->len, + mime_type); + empathy_contact_set_avatar (contact, avatar); + empathy_avatar_unref (avatar); +} + +static void +contact_factory_request_everything (ContactFactoryAccountData *account_data, + GArray *handles) +{ + if (account_data->presence_iface) { + account_data->nb_pending_calls++; + tp_conn_iface_presence_get_presence_async (account_data->presence_iface, + handles, + contact_factory_get_presence_cb, + account_data); + } + + if (account_data->aliasing_iface) { + RequestAliasesData *data; + + account_data->nb_pending_calls++; + data = g_slice_new (RequestAliasesData); + data->account_data = account_data; + data->handles = g_memdup (handles->data, handles->len * sizeof (guint)); + + tp_conn_iface_aliasing_request_aliases_async (account_data->aliasing_iface, + handles, + contact_factory_request_aliases_cb, + data); + } + + if (account_data->avatars_iface) { + account_data->nb_pending_calls++; + tp_conn_iface_avatars_request_avatars_async (account_data->avatars_iface, + handles, + contact_factory_request_avatars_cb, + account_data); + } +} + +static void +contact_factory_request_handles_cb (DBusGProxy *proxy, + GArray *handles, + GError *error, + gpointer user_data) +{ + RequestHandlesData *data = user_data; + GList *l; + guint i = 0; + + if (error) { + empathy_debug (DEBUG_DOMAIN, "Failed to request handles: %s", + error->message); + goto OUT; + } + + for (l = data->contacts; l; l = l->next) { + guint handle; + + handle = g_array_index (handles, guint, i); + empathy_contact_set_handle (l->data, handle); + if (handle == data->account_data->self_handle) { + empathy_contact_set_is_user (l->data, TRUE); + } + + i++; + } + + contact_factory_request_everything (data->account_data, handles); + +OUT: + g_list_foreach (data->contacts, (GFunc) g_object_unref, NULL); + g_list_free (data->contacts); + contact_factory_account_data_return_call (data->account_data); + g_slice_free (RequestHandlesData, data); +} + +static void +contact_factory_disconnect_contact_foreach (gpointer data, + gpointer user_data) +{ + EmpathyContact *contact = data; + + empathy_contact_set_presence (contact, NULL); + empathy_contact_set_handle (contact, 0); +} + +static void +contact_factory_destroy_cb (TpConn *tp_conn, + ContactFactoryAccountData *account_data) +{ + empathy_debug (DEBUG_DOMAIN, "Account disconnected or CM crashed"); + + g_object_unref (account_data->tp_conn); + account_data->tp_conn = NULL; + account_data->aliasing_iface = NULL; + account_data->avatars_iface = NULL; + account_data->presence_iface = NULL; + + g_list_foreach (account_data->contacts, + contact_factory_disconnect_contact_foreach, + account_data); +} + +static void +contact_factory_account_data_disconnect (ContactFactoryAccountData *account_data) +{ + if (account_data->aliasing_iface) { + dbus_g_proxy_disconnect_signal (account_data->aliasing_iface, + "AliasesChanged", + G_CALLBACK (contact_factory_aliases_changed_cb), + account_data); + } + if (account_data->avatars_iface) { + dbus_g_proxy_disconnect_signal (account_data->avatars_iface, + "AvatarUpdated", + G_CALLBACK (contact_factory_avatar_updated_cb), + account_data); + dbus_g_proxy_disconnect_signal (account_data->avatars_iface, + "AvatarRetrieved", + G_CALLBACK (contact_factory_avatar_retrieved_cb), + account_data); + } + if (account_data->presence_iface) { + dbus_g_proxy_disconnect_signal (account_data->presence_iface, + "PresenceUpdate", + G_CALLBACK (contact_factory_presence_update_cb), + account_data); + } + if (account_data->tp_conn) { + g_signal_handlers_disconnect_by_func (account_data->tp_conn, + contact_factory_destroy_cb, + account_data); + } +} + +static void +contact_factory_account_data_update (ContactFactoryAccountData *account_data) +{ + EmpathyContactFactory *factory = account_data->factory; + EmpathyContactFactoryPriv *priv = GET_PRIV (factory); + McAccount *account = account_data->account; + TpConn *tp_conn = NULL; + RequestHandlesData *data; + const gchar **contact_ids; + guint i; + GList *l; + GError *error; + + if (account_data->account) { + guint status; + + /* status == 0 means the status is CONNECTED */ + status = mission_control_get_connection_status (priv->mc, + account, NULL); + if (status == 0) { + tp_conn = mission_control_get_connection (priv->mc, + account, NULL); + } + } + + if (!tp_conn) { + /* We are not connected anymore, remove the old connection */ + contact_factory_account_data_disconnect (account_data); + if (account_data->tp_conn) { + contact_factory_destroy_cb (account_data->tp_conn, + account_data); + } + return; + } + else if (account_data->tp_conn) { + /* We were connected and we still are connected, nothing + * changed so nothing to do. */ + g_object_unref (tp_conn); + return; + } + + /* We got a new connection */ + account_data->tp_conn = tp_conn; + account_data->aliasing_iface = tp_conn_get_interface (tp_conn, + TELEPATHY_CONN_IFACE_ALIASING_QUARK); + account_data->avatars_iface = tp_conn_get_interface (tp_conn, + TELEPATHY_CONN_IFACE_AVATARS_QUARK); + account_data->presence_iface = tp_conn_get_interface (tp_conn, + TELEPATHY_CONN_IFACE_PRESENCE_QUARK); + + /* Connect signals */ + if (account_data->aliasing_iface) { + dbus_g_proxy_connect_signal (account_data->aliasing_iface, + "AliasesChanged", + G_CALLBACK (contact_factory_aliases_changed_cb), + account_data, NULL); + } + if (account_data->avatars_iface) { + dbus_g_proxy_connect_signal (account_data->avatars_iface, + "AvatarUpdated", + G_CALLBACK (contact_factory_avatar_updated_cb), + account_data, NULL); + dbus_g_proxy_connect_signal (account_data->avatars_iface, + "AvatarRetrieved", + G_CALLBACK (contact_factory_avatar_retrieved_cb), + account_data, NULL); + } + if (account_data->presence_iface) { + dbus_g_proxy_connect_signal (account_data->presence_iface, + "PresenceUpdate", + G_CALLBACK (contact_factory_presence_update_cb), + account_data, NULL); + } + g_signal_connect (tp_conn, "destroy", + G_CALLBACK (contact_factory_destroy_cb), + account_data); + + /* Get our own handle */ + if (!tp_conn_get_self_handle (DBUS_G_PROXY (account_data->tp_conn), + &account_data->self_handle, + &error)) { + empathy_debug (DEBUG_DOMAIN, "GetSelfHandle Error: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } + + /* Request new handles for all contacts */ + if (account_data->contacts) { + data = g_slice_new (RequestHandlesData); + data->account_data = account_data; + data->contacts = g_list_copy (account_data->contacts); + g_list_foreach (data->contacts, (GFunc) g_object_ref, NULL); + + i = g_list_length (data->contacts); + contact_ids = g_new0 (const gchar*, i + 1); + i = 0; + for (l = data->contacts; l; l = l->next) { + contact_ids[i] = empathy_contact_get_id (l->data); + i++; + } + + account_data->nb_pending_calls++; + tp_conn_request_handles_async (DBUS_G_PROXY (account_data->tp_conn), + TP_HANDLE_TYPE_CONTACT, + contact_ids, + contact_factory_request_handles_cb, + data); + g_free (contact_ids); + } +} + +static void +contact_factory_weak_notify (gpointer data, + GObject *where_the_object_was) +{ + ContactFactoryAccountData *account_data = data; + + empathy_debug (DEBUG_DOMAIN, "Remove finalized contact %p", + where_the_object_was); + + account_data->contacts = g_list_remove (account_data->contacts, + where_the_object_was); + if (!account_data->contacts) { + EmpathyContactFactoryPriv *priv; + + priv = GET_PRIV (account_data->factory); + + g_hash_table_remove (priv->accounts, account_data->account); + } +} + +static void +contact_factory_remove_foreach (gpointer data, + gpointer user_data) +{ + ContactFactoryAccountData *account_data = user_data; + EmpathyContact *contact = data; + + g_object_weak_unref (G_OBJECT (contact), + contact_factory_weak_notify, + account_data); +} + +static ContactFactoryAccountData * +contact_factory_account_data_new (EmpathyContactFactory *factory, + McAccount *account) +{ + ContactFactoryAccountData *account_data; + + account_data = g_slice_new0 (ContactFactoryAccountData); + account_data->factory = factory; + account_data->account = g_object_ref (account); + + contact_factory_account_data_update (account_data); + + return account_data; +} + +static void +contact_factory_account_data_free (gpointer data) +{ + ContactFactoryAccountData *account_data = data; + + contact_factory_account_data_disconnect (account_data); + + if (account_data->contacts) { + g_list_foreach (account_data->contacts, + contact_factory_remove_foreach, + account_data); + g_list_free (account_data->contacts); + account_data->contacts = NULL; + } + + if (account_data->account) { + g_object_unref (account_data->account); + account_data->account = NULL; + } + + if (account_data->tp_conn) { + g_object_unref (account_data->tp_conn); + account_data->tp_conn = NULL; + account_data->aliasing_iface = NULL; + account_data->avatars_iface = NULL; + account_data->presence_iface = NULL; + } + + /* Keep the struct alive if we have calls in flight, it will be + * destroyed once all calls returned. */ + if (account_data->nb_pending_calls == 0) { + g_slice_free (ContactFactoryAccountData, account_data); + } +} + +static void +contact_factory_status_changed_cb (MissionControl *mc, + TelepathyConnectionStatus status, + McPresence presence, + TelepathyConnectionStatusReason reason, + const gchar *unique_name, + EmpathyContactFactory *factory) +{ + EmpathyContactFactoryPriv *priv = GET_PRIV (factory); + ContactFactoryAccountData *account_data; + McAccount *account; + + account = mc_account_lookup (unique_name); + account_data = g_hash_table_lookup (priv->accounts, account); + if (account_data) { + contact_factory_account_data_update (account_data); + } + g_object_unref (account); +} + +static ContactFactoryAccountData * +contact_factory_account_data_get (EmpathyContactFactory *factory, + McAccount *account) +{ + EmpathyContactFactoryPriv *priv = GET_PRIV (factory); + ContactFactoryAccountData *account_data; + + account_data = g_hash_table_lookup (priv->accounts, account); + if (!account_data) { + account_data = contact_factory_account_data_new (factory, account); + g_hash_table_insert (priv->accounts, + g_object_ref (account), + account_data); + } + + return account_data; +} + +static void +contact_factory_account_data_add_contact (ContactFactoryAccountData *account_data, + EmpathyContact *contact) +{ + g_object_weak_ref (G_OBJECT (contact), + contact_factory_weak_notify, + account_data); + account_data->contacts = g_list_prepend (account_data->contacts, contact); + + if (!account_data->presence_iface) { + EmpathyPresence *presence; + + /* We have no presence iface, set default presence + * to available */ + presence = empathy_presence_new_full (MC_PRESENCE_AVAILABLE, + NULL); + + empathy_contact_set_presence (contact, presence); + g_object_unref (presence); + } + + empathy_debug (DEBUG_DOMAIN, "Contact added: %s (%d)", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact)); +} + +static void +contact_factory_hold_handles_cb (DBusGProxy *proxy, + GError *error, + gpointer userdata) +{ + if (error) { + empathy_debug (DEBUG_DOMAIN, "Failed to hold handles: %s", + error->message); + } +} + +EmpathyContact * +empathy_contact_factory_get_user (EmpathyContactFactory *factory, + McAccount *account) +{ + ContactFactoryAccountData *account_data; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_FACTORY (factory), NULL); + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + + account_data = contact_factory_account_data_get (factory, account); + + return empathy_contact_factory_get_from_handle (factory, account, + account_data->self_handle); +} + +EmpathyContact * +empathy_contact_factory_get_from_id (EmpathyContactFactory *factory, + McAccount *account, + const gchar *id) +{ + ContactFactoryAccountData *account_data; + EmpathyContact *contact; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_FACTORY (factory), NULL); + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (id != NULL, NULL); + + /* Check if the contact already exists */ + account_data = contact_factory_account_data_get (factory, account); + contact = contact_factory_account_data_find_by_id (account_data, id); + if (contact) { + return g_object_ref (contact); + } + + /* Create new contact */ + contact = g_object_new (EMPATHY_TYPE_CONTACT, + "account", account, + "id", id, + NULL); + contact_factory_account_data_add_contact (account_data, contact); + + /* If the account is connected, request contact's handle */ + if (account_data->tp_conn) { + RequestHandlesData *data; + const gchar *contact_ids[] = {id, NULL}; + + account_data->nb_pending_calls++; + data = g_slice_new (RequestHandlesData); + data->account_data = account_data; + data->contacts = g_list_prepend (NULL, g_object_ref (contact)); + tp_conn_request_handles_async (DBUS_G_PROXY (account_data->tp_conn), + TP_HANDLE_TYPE_CONTACT, + contact_ids, + contact_factory_request_handles_cb, + data); + } + + return contact; +} + +EmpathyContact * +empathy_contact_factory_get_from_handle (EmpathyContactFactory *factory, + McAccount *account, + guint handle) +{ + EmpathyContact *contact; + GArray *handles; + GList *contacts; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_FACTORY (factory), NULL); + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + + handles = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_val (handles, handle); + + contacts = empathy_contact_factory_get_from_handles (factory, account, handles); + g_array_free (handles, TRUE); + + contact = contacts ? contacts->data : NULL; + g_list_free (contacts); + + return contact; +} + +GList * +empathy_contact_factory_get_from_handles (EmpathyContactFactory *factory, + McAccount *account, + GArray *handles) +{ + ContactFactoryAccountData *account_data; + GList *contacts = NULL; + GArray *new_handles; + gchar **handles_names; + guint i; + GError *error = NULL; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_FACTORY (factory), NULL); + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (handles != NULL, NULL); + + /* Search all contacts we already have */ + account_data = contact_factory_account_data_get (factory, account); + new_handles = g_array_new (FALSE, FALSE, sizeof (guint)); + for (i = 0; i < handles->len; i++) { + EmpathyContact *contact; + guint handle; + + handle = g_array_index (handles, guint, i); + if (handle == 0) { + continue; + } + + contact = contact_factory_account_data_find_by_handle (account_data, handle); + if (contact) { + contacts = g_list_prepend (contacts, g_object_ref (contact)); + } else { + g_array_append_val (new_handles, handle); + } + } + + if (new_handles->len == 0) { + g_array_free (new_handles, TRUE); + return contacts; + } + + /* Get the IDs of all new handles */ + if (!tp_conn_inspect_handles (DBUS_G_PROXY (account_data->tp_conn), + TP_HANDLE_TYPE_CONTACT, + new_handles, + &handles_names, + &error)) { + empathy_debug (DEBUG_DOMAIN, + "Couldn't inspect contact: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + g_array_free (new_handles, TRUE); + return contacts; + } + + /* Create new contacts */ + for (i = 0; i < new_handles->len; i++) { + EmpathyContact *contact; + gchar *id; + guint handle; + gboolean is_user; + + id = handles_names[i]; + handle = g_array_index (new_handles, guint, i); + + is_user = (handle == account_data->self_handle); + contact = g_object_new (EMPATHY_TYPE_CONTACT, + "account", account, + "handle", handle, + "id", id, + "is-user", is_user, + NULL); + contact_factory_account_data_add_contact (account_data, + contact); + contacts = g_list_prepend (contacts, contact); + g_free (id); + } + g_free (handles_names); + + /* Hold all new handles. */ + tp_conn_hold_handles_async (DBUS_G_PROXY (account_data->tp_conn), + TP_HANDLE_TYPE_CONTACT, + new_handles, + contact_factory_hold_handles_cb, + NULL); + + contact_factory_request_everything (account_data, new_handles); + + g_array_free (new_handles, TRUE); + + return contacts; +} + +void +empathy_contact_factory_set_name (EmpathyContactFactory *factory, + EmpathyContact *contact, + const gchar *name) +{ + ContactFactoryAccountData *account_data; + McAccount *account; + GHashTable *new_alias; + guint handle; + + g_return_if_fail (EMPATHY_IS_CONTACT_FACTORY (factory)); + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + + account = empathy_contact_get_account (contact); + account_data = contact_factory_account_data_get (factory, account); + + if (!account_data->aliasing_iface) { + return; + } + + handle = empathy_contact_get_handle (contact); + + empathy_debug (DEBUG_DOMAIN, "Setting alias for contact %s (%d) to %s", + empathy_contact_get_id (contact), + handle, name); + + new_alias = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_free); + + g_hash_table_insert (new_alias, + GUINT_TO_POINTER (handle), + g_strdup (name)); + + account_data->nb_pending_calls++; + tp_conn_iface_aliasing_set_aliases_async (account_data->aliasing_iface, + new_alias, + contact_factory_set_aliases_cb, + account_data); + + g_hash_table_destroy (new_alias); +} + +static void +contact_factory_finalize (GObject *object) +{ + EmpathyContactFactoryPriv *priv; + + priv = GET_PRIV (object); + + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), + "AccountStatusChanged", + G_CALLBACK (contact_factory_status_changed_cb), + object); + + g_hash_table_destroy (priv->accounts); + g_object_unref (priv->mc); + + G_OBJECT_CLASS (empathy_contact_factory_parent_class)->finalize (object); +} + +static void +empathy_contact_factory_class_init (EmpathyContactFactoryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = contact_factory_finalize; + + g_type_class_add_private (object_class, sizeof (EmpathyContactFactoryPriv)); +} + +static void +empathy_contact_factory_init (EmpathyContactFactory *factory) +{ + EmpathyContactFactoryPriv *priv; + + priv = GET_PRIV (factory); + + priv->mc = empathy_mission_control_new (); + priv->accounts = g_hash_table_new_full (empathy_account_hash, + empathy_account_equal, + g_object_unref, + contact_factory_account_data_free); + + dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), + "AccountStatusChanged", + G_CALLBACK (contact_factory_status_changed_cb), + factory, NULL); +} + +EmpathyContactFactory * +empathy_contact_factory_new (void) +{ + static EmpathyContactFactory *factory = NULL; + + if (!factory) { + factory = g_object_new (EMPATHY_TYPE_CONTACT_FACTORY, NULL); + g_object_add_weak_pointer (G_OBJECT (factory), (gpointer) &factory); + } else { + g_object_ref (factory); + } + + return factory; +} + diff --git a/libempathy/empathy-contact-factory.h b/libempathy/empathy-contact-factory.h new file mode 100644 index 000000000..1379371ab --- /dev/null +++ b/libempathy/empathy-contact-factory.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_CONTACT_FACTORY_H__ +#define __EMPATHY_CONTACT_FACTORY_H__ + +#include <glib.h> + +#include <libmissioncontrol/mc-account.h> + +#include "empathy-contact.h" + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CONTACT_FACTORY (empathy_contact_factory_get_type ()) +#define EMPATHY_CONTACT_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CONTACT_FACTORY, EmpathyContactFactory)) +#define EMPATHY_CONTACT_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CONTACT_FACTORY, EmpathyContactFactoryClass)) +#define EMPATHY_IS_CONTACT_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CONTACT_FACTORY)) +#define EMPATHY_IS_CONTACT_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CONTACT_FACTORY)) +#define EMPATHY_CONTACT_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CONTACT_FACTORY, EmpathyContactFactoryClass)) + +typedef struct _EmpathyContactFactory EmpathyContactFactory; +typedef struct _EmpathyContactFactoryClass EmpathyContactFactoryClass; +typedef struct _EmpathyContactFactoryPriv EmpathyContactFactoryPriv; + +struct _EmpathyContactFactory { + GObject parent; +}; + +struct _EmpathyContactFactoryClass { + GObjectClass parent_class; +}; + +GType empathy_contact_factory_get_type (void) G_GNUC_CONST; +EmpathyContactFactory *empathy_contact_factory_new (void); +EmpathyContact * empathy_contact_factory_get_user (EmpathyContactFactory *factory, + McAccount *account); +EmpathyContact * empathy_contact_factory_get_from_id (EmpathyContactFactory *factory, + McAccount *account, + const gchar *id); +EmpathyContact * empathy_contact_factory_get_from_handle (EmpathyContactFactory *factory, + McAccount *account, + guint handle); +GList * empathy_contact_factory_get_from_handles (EmpathyContactFactory *factory, + McAccount *account, + GArray *handles); +void empathy_contact_factory_set_name (EmpathyContactFactory *factory, + EmpathyContact *contact, + const gchar *name); + +G_END_DECLS + +#endif /* __EMPATHY_CONTACT_FACTORY_H__ */ diff --git a/libempathy/empathy-contact-list.c b/libempathy/empathy-contact-list.c index cc388d7ec..80308314e 100644 --- a/libempathy/empathy-contact-list.c +++ b/libempathy/empathy-contact-list.c @@ -53,149 +53,154 @@ contact_list_base_init (gpointer klass) static gboolean initialized = FALSE; if (!initialized) { - g_signal_new ("contact-added", + g_signal_new ("members-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, + empathy_marshal_VOID__OBJECT_OBJECT_UINT_STRING_BOOLEAN, G_TYPE_NONE, - 1, EMPATHY_TYPE_CONTACT); + 5, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT, + G_TYPE_UINT, G_TYPE_STRING, G_TYPE_BOOLEAN); - g_signal_new ("contact-removed", + g_signal_new ("pendings-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, + empathy_marshal_VOID__OBJECT_OBJECT_UINT_STRING_BOOLEAN, G_TYPE_NONE, - 1, EMPATHY_TYPE_CONTACT); + 5, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT, + G_TYPE_UINT, G_TYPE_STRING, G_TYPE_BOOLEAN); - g_signal_new ("local-pending", + g_signal_new ("groups-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, - empathy_marshal_VOID__OBJECT_STRING, + empathy_marshal_VOID__OBJECT_STRING_BOOLEAN, G_TYPE_NONE, - 2, EMPATHY_TYPE_CONTACT, G_TYPE_STRING); + 3, EMPATHY_TYPE_CONTACT, G_TYPE_STRING, G_TYPE_BOOLEAN); initialized = TRUE; } } -EmpathyContactListInfo * -empathy_contact_list_info_new (EmpathyContact *contact, - const gchar *message) -{ - EmpathyContactListInfo *info; - - g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); - - info = g_slice_new0 (EmpathyContactListInfo); - info->contact = g_object_ref (contact); - info->message = g_strdup (message); - - return info; -} - void -empathy_contact_list_info_free (EmpathyContactListInfo *info) +empathy_contact_list_add (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *message) { - if (!info) { - return; - } + g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list)); + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - if (info->contact) { - g_object_unref (info->contact); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->add) { + EMPATHY_CONTACT_LIST_GET_IFACE (list)->add (list, contact, message); } - g_free (info->message); - - g_slice_free (EmpathyContactListInfo, info); } void -empathy_contact_list_setup (EmpathyContactList *list) +empathy_contact_list_remove (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *message) { g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list)); + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->setup) { - EMPATHY_CONTACT_LIST_GET_IFACE (list)->setup (list); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove) { + EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove (list, contact, message); } } -EmpathyContact * -empathy_contact_list_find (EmpathyContactList *list, - const gchar *id) +GList * +empathy_contact_list_get_members (EmpathyContactList *list) { g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); - if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->find) { - return EMPATHY_CONTACT_LIST_GET_IFACE (list)->find (list, id); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_members) { + return EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_members (list); } return NULL; } -void -empathy_contact_list_add (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message) +GList * +empathy_contact_list_get_pendings (EmpathyContactList *list) { - g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list)); + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); - if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->add) { - EMPATHY_CONTACT_LIST_GET_IFACE (list)->add (list, contact, message); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_pendings) { + return EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_pendings (list); } + + return NULL; } -void -empathy_contact_list_remove (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message) +GList * +empathy_contact_list_get_all_groups (EmpathyContactList *list) { - g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list)); + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); - if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove) { - EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove (list, contact, message); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_all_groups) { + return EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_all_groups (list); } + + return NULL; } GList * -empathy_contact_list_get_members (EmpathyContactList *list) +empathy_contact_list_get_groups (EmpathyContactList *list, + EmpathyContact *contact) { g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); - if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_members) { - return EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_members (list); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_groups) { + return EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_groups (list, contact); } return NULL; } -GList * -empathy_contact_list_get_local_pending (EmpathyContactList *list) +void +empathy_contact_list_add_to_group (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *group) { - g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); + g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list)); + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + g_return_if_fail (group != NULL); - if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_local_pending) { - return EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_local_pending (list); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->add_to_group) { + EMPATHY_CONTACT_LIST_GET_IFACE (list)->add_to_group (list, contact, group); } +} - return NULL; +void +empathy_contact_list_remove_from_group (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *group) +{ + g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list)); + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + g_return_if_fail (group != NULL); + + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove_from_group) { + EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove_from_group (list, contact, group); + } } void -empathy_contact_list_process_pending (EmpathyContactList *list, - EmpathyContact *contact, - gboolean accept) +empathy_contact_list_rename_group (EmpathyContactList *list, + const gchar *old_group, + const gchar *new_group) { g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list)); + g_return_if_fail (old_group != NULL); + g_return_if_fail (new_group != NULL); - if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->process_pending) { - EMPATHY_CONTACT_LIST_GET_IFACE (list)->process_pending (list, - contact, - accept); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->rename_group) { + EMPATHY_CONTACT_LIST_GET_IFACE (list)->rename_group (list, old_group, new_group); } } diff --git a/libempathy/empathy-contact-list.h b/libempathy/empathy-contact-list.h index 959f5c51f..4b027f083 100644 --- a/libempathy/empathy-contact-list.h +++ b/libempathy/empathy-contact-list.h @@ -26,6 +26,7 @@ #include <glib-object.h> #include "empathy-contact.h" +#include "empathy-tp-group.h" G_BEGIN_DECLS @@ -37,49 +38,53 @@ G_BEGIN_DECLS typedef struct _EmpathyContactList EmpathyContactList; typedef struct _EmpathyContactListIface EmpathyContactListIface; -typedef struct { - EmpathyContact *contact; - gchar *message; -} EmpathyContactListInfo; - struct _EmpathyContactListIface { GTypeInterface base_iface; /* VTabled */ - void (*setup) (EmpathyContactList *list); - EmpathyContact * (*find) (EmpathyContactList *list, - const gchar *id); - void (*add) (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message); - void (*remove) (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message); - GList * (*get_members) (EmpathyContactList *list); - GList * (*get_local_pending) (EmpathyContactList *list); - void (*process_pending) (EmpathyContactList *list, - EmpathyContact *contact, - gboolean accept); + void (*add) (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *message); + void (*remove) (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *message); + GList * (*get_members) (EmpathyContactList *list); + GList * (*get_pendings) (EmpathyContactList *list); + GList * (*get_all_groups) (EmpathyContactList *list); + GList * (*get_groups) (EmpathyContactList *list, + EmpathyContact *contact); + void (*add_to_group) (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *group); + void (*remove_from_group) (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *group); + void (*rename_group) (EmpathyContactList *list, + const gchar *old_group, + const gchar *new_group); }; -GType empathy_contact_list_get_type (void) G_GNUC_CONST; -EmpathyContactListInfo *empathy_contact_list_info_new (EmpathyContact *contact, - const gchar *message); -void empathy_contact_list_info_free (EmpathyContactListInfo *info); -void empathy_contact_list_setup (EmpathyContactList *list); -EmpathyContact * empathy_contact_list_find (EmpathyContactList *list, - const gchar *id); -void empathy_contact_list_add (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message); -void empathy_contact_list_remove (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message); -GList * empathy_contact_list_get_members (EmpathyContactList *list); -GList * empathy_contact_list_get_local_pending (EmpathyContactList *list); -void empathy_contact_list_process_pending (EmpathyContactList *list, - EmpathyContact *contact, - gboolean accept); +GType empathy_contact_list_get_type (void) G_GNUC_CONST; +void empathy_contact_list_add (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *message); +void empathy_contact_list_remove (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *message); +GList * empathy_contact_list_get_members (EmpathyContactList *list); +GList * empathy_contact_list_get_pendings (EmpathyContactList *list); +GList * empathy_contact_list_get_all_groups (EmpathyContactList *list); +GList * empathy_contact_list_get_groups (EmpathyContactList *list, + EmpathyContact *contact); +void empathy_contact_list_add_to_group (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *group); +void empathy_contact_list_remove_from_group (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *group); +void empathy_contact_list_rename_group (EmpathyContactList *list, + const gchar *old_group, + const gchar *new_group); G_END_DECLS diff --git a/libempathy/empathy-contact-manager.c b/libempathy/empathy-contact-manager.c index cc00108c8..575814fab 100644 --- a/libempathy/empathy-contact-manager.c +++ b/libempathy/empathy-contact-manager.c @@ -39,100 +39,171 @@ struct _EmpathyContactManagerPriv { GHashTable *lists; MissionControl *mc; - gboolean setup; }; -typedef struct { - const gchar *old_group; - const gchar *new_group; -} ContactManagerRenameGroupData; - -typedef struct { - EmpathyContact *contact; - const gchar *id; -} ContactManagerFindData; - -static void empathy_contact_manager_class_init (EmpathyContactManagerClass *klass); -static void contact_manager_iface_init (EmpathyContactListIface *iface); -static void empathy_contact_manager_init (EmpathyContactManager *manager); -static void contact_manager_finalize (GObject *object); -static void contact_manager_setup (EmpathyContactList *manager); -static EmpathyContact *contact_manager_find (EmpathyContactList *manager, - const gchar *id); -static void contact_manager_add (EmpathyContactList *manager, - EmpathyContact *contact, - const gchar *message); -static void contact_manager_remove (EmpathyContactList *manager, - EmpathyContact *contact, - const gchar *message); -static GList * contact_manager_get_members (EmpathyContactList *manager); -static GList * contact_manager_get_local_pending (EmpathyContactList *manager); -static void contact_manager_process_pending (EmpathyContactList *manager, - EmpathyContact *contact, - gboolean accept); -static void contact_manager_setup_foreach (McAccount *account, - EmpathyTpContactList *list, - EmpathyContactManager *manager); -static gboolean contact_manager_find_foreach (McAccount *account, - EmpathyTpContactList *list, - ContactManagerFindData *data); -static void contact_manager_add_account (EmpathyContactManager *manager, - McAccount *account); -static void contact_manager_added_cb (EmpathyTpContactList *list, - EmpathyContact *contact, - EmpathyContactManager *manager); -static void contact_manager_removed_cb (EmpathyTpContactList *list, - EmpathyContact *contact, - EmpathyContactManager *manager); -static void contact_manager_local_pending_cb (EmpathyTpContactList *list, - EmpathyContact *contact, - const gchar *message, - EmpathyContactManager *manager); -static void contact_manager_destroy_cb (EmpathyTpContactList *list, - EmpathyContactManager *manager); -static void contact_manager_rename_group_foreach (McAccount *account, - EmpathyTpContactList *list, - ContactManagerRenameGroupData *data); -static void contact_manager_get_groups_foreach (McAccount *account, - EmpathyTpContactList *list, - GList **all_groups); -static void contact_manager_get_members_foreach (McAccount *account, - EmpathyTpContactList *list, - GList **contacts); -static void contact_manager_get_local_pending_foreach (McAccount *account, - EmpathyTpContactList *list, - GList **contacts); -static void contact_manager_status_changed_cb (MissionControl *mc, - TelepathyConnectionStatus status, - McPresence presence, - TelepathyConnectionStatusReason reason, - const gchar *unique_name, - EmpathyContactManager *manager); +static void empathy_contact_manager_class_init (EmpathyContactManagerClass *klass); +static void empathy_contact_manager_init (EmpathyContactManager *manager); +static void contact_manager_iface_init (EmpathyContactListIface *iface); G_DEFINE_TYPE_WITH_CODE (EmpathyContactManager, empathy_contact_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST, contact_manager_iface_init)); static void -empathy_contact_manager_class_init (EmpathyContactManagerClass *klass) +contact_manager_members_changed_cb (EmpathyTpContactList *list, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + gchar *message, + gboolean is_member, + EmpathyContactManager *manager) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_signal_emit_by_name (manager, "members-changed", + contact, actor, reason, message, is_member); +} - object_class->finalize = contact_manager_finalize; +static void +contact_manager_pendings_changed_cb (EmpathyTpContactList *list, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + gchar *message, + gboolean is_pending, + EmpathyContactManager *manager) +{ + g_signal_emit_by_name (manager, "pendings-changed", + contact, actor, reason, message, is_pending); +} - g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv)); +static void +contact_manager_groups_changed_cb (EmpathyTpContactList *list, + EmpathyContact *contact, + gchar *group, + gboolean is_member, + EmpathyContactManager *manager) +{ + g_signal_emit_by_name (manager, "groups-changed", + contact, group, is_member); } +static void contact_manager_destroy_cb (EmpathyTpContactList *list, + EmpathyContactManager *manager); + static void -contact_manager_iface_init (EmpathyContactListIface *iface) +contact_manager_disconnect_foreach (gpointer key, + gpointer value, + gpointer user_data) { - iface->setup = contact_manager_setup; - iface->find = contact_manager_find; - iface->add = contact_manager_add; - iface->remove = contact_manager_remove; - iface->get_members = contact_manager_get_members; - iface->get_local_pending = contact_manager_get_local_pending; - iface->process_pending = contact_manager_process_pending; + EmpathyTpContactList *list = value; + EmpathyContactManager *manager = user_data; + + /* Disconnect signals from the list */ + g_signal_handlers_disconnect_by_func (list, + contact_manager_members_changed_cb, + manager); + g_signal_handlers_disconnect_by_func (list, + contact_manager_pendings_changed_cb, + manager); + g_signal_handlers_disconnect_by_func (list, + contact_manager_groups_changed_cb, + manager); + g_signal_handlers_disconnect_by_func (list, + contact_manager_destroy_cb, + manager); +} + +static void +contact_manager_destroy_cb (EmpathyTpContactList *list, + EmpathyContactManager *manager) +{ + EmpathyContactManagerPriv *priv = GET_PRIV (manager); + McAccount *account; + + account = empathy_tp_contact_list_get_account (list); + + empathy_debug (DEBUG_DOMAIN, "Removing account: %s", + mc_account_get_display_name (account)); + + contact_manager_disconnect_foreach (account, list, manager); + g_hash_table_remove (priv->lists, account); +} + +static void +contact_manager_add_account (EmpathyContactManager *manager, + McAccount *account) +{ + EmpathyContactManagerPriv *priv = GET_PRIV (manager); + EmpathyTpContactList *list; + + if (g_hash_table_lookup (priv->lists, account)) { + return; + } + + empathy_debug (DEBUG_DOMAIN, "Adding new account: %s", + mc_account_get_display_name (account)); + + list = empathy_tp_contact_list_new (account); + if (!list) { + return; + } + + g_hash_table_insert (priv->lists, g_object_ref (account), list); + + /* Connect signals */ + g_signal_connect (list, "members-changed", + G_CALLBACK (contact_manager_members_changed_cb), + manager); + g_signal_connect (list, "pendings-changed", + G_CALLBACK (contact_manager_pendings_changed_cb), + manager); + g_signal_connect (list, "groups-changed", + G_CALLBACK (contact_manager_groups_changed_cb), + manager); + g_signal_connect (list, "destroy", + G_CALLBACK (contact_manager_destroy_cb), + manager); +} + +static void +contact_manager_status_changed_cb (MissionControl *mc, + TelepathyConnectionStatus status, + McPresence presence, + TelepathyConnectionStatusReason reason, + const gchar *unique_name, + EmpathyContactManager *manager) +{ + McAccount *account; + + if (status != TP_CONN_STATUS_CONNECTED) { + /* We only care about newly connected accounts */ + return; + } + + account = mc_account_lookup (unique_name); + contact_manager_add_account (manager, account); + g_object_unref (account); +} + +static void +contact_manager_finalize (GObject *object) +{ + EmpathyContactManagerPriv *priv = GET_PRIV (object); + + g_hash_table_foreach (priv->lists, + contact_manager_disconnect_foreach, + object); + g_hash_table_destroy (priv->lists); + g_object_unref (priv->mc); +} + +static void +empathy_contact_manager_class_init (EmpathyContactManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = contact_manager_finalize; + + g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv)); } static void @@ -158,27 +229,12 @@ empathy_contact_manager_init (EmpathyContactManager *manager) /* Get ContactList for existing connections */ accounts = mission_control_get_online_connections (priv->mc, NULL); for (l = accounts; l; l = l->next) { - McAccount *account; - - account = l->data; - contact_manager_add_account (manager, account); - - g_object_unref (account); + contact_manager_add_account (manager, l->data); + g_object_unref (l->data); } g_slist_free (accounts); } -static void -contact_manager_finalize (GObject *object) -{ - EmpathyContactManagerPriv *priv; - - priv = GET_PRIV (object); - - g_hash_table_destroy (priv->lists); - g_object_unref (priv->mc); -} - EmpathyContactManager * empathy_contact_manager_new (void) { @@ -194,62 +250,28 @@ empathy_contact_manager_new (void) return manager; } -static void -contact_manager_setup (EmpathyContactList *manager) -{ - EmpathyContactManagerPriv *priv; - - g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); - - priv = GET_PRIV (manager); - - if (priv->setup) { - /* Already done */ - return; - } - - g_hash_table_foreach (priv->lists, - (GHFunc) contact_manager_setup_foreach, - manager); - - priv->setup = TRUE; -} - -static EmpathyContact * -contact_manager_find (EmpathyContactList *manager, - const gchar *id) +EmpathyTpContactList * +empathy_contact_manager_get_list (EmpathyContactManager *manager, + McAccount *account) { - EmpathyContactManagerPriv *priv; - ContactManagerFindData data; + EmpathyContactManagerPriv *priv = GET_PRIV (manager); g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - g_return_val_if_fail (id != NULL, NULL); - - priv = GET_PRIV (manager); - - data.contact = NULL; - data.id = id; - - g_hash_table_find (priv->lists, - (GHRFunc) contact_manager_find_foreach, - &data); + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); - return data.contact; + return g_hash_table_lookup (priv->lists, account); } static void contact_manager_add (EmpathyContactList *manager, - EmpathyContact *contact, + EmpathyContact *contact, const gchar *message) { - EmpathyContactManagerPriv *priv; + EmpathyContactManagerPriv *priv = GET_PRIV (manager); EmpathyContactList *list; McAccount *account; g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); - g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - - priv = GET_PRIV (manager); account = empathy_contact_get_account (contact); list = g_hash_table_lookup (priv->lists, account); @@ -264,14 +286,11 @@ contact_manager_remove (EmpathyContactList *manager, EmpathyContact *contact, const gchar *message) { - EmpathyContactManagerPriv *priv; + EmpathyContactManagerPriv *priv = GET_PRIV (manager); EmpathyContactList *list; McAccount *account; g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); - g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - - priv = GET_PRIV (manager); account = empathy_contact_get_account (contact); list = g_hash_table_lookup (priv->lists, account); @@ -281,16 +300,25 @@ contact_manager_remove (EmpathyContactList *manager, } } +static void +contact_manager_get_members_foreach (McAccount *account, + EmpathyTpContactList *list, + GList **contacts) +{ + GList *l; + + l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list)); + *contacts = g_list_concat (*contacts, l); +} + static GList * contact_manager_get_members (EmpathyContactList *manager) { - EmpathyContactManagerPriv *priv; + EmpathyContactManagerPriv *priv = GET_PRIV (manager); GList *contacts = NULL; g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - priv = GET_PRIV (manager); - g_hash_table_foreach (priv->lists, (GHFunc) contact_manager_get_members_foreach, &contacts); @@ -298,337 +326,169 @@ contact_manager_get_members (EmpathyContactList *manager) return contacts; } -static GList * -contact_manager_get_local_pending (EmpathyContactList *manager) -{ - EmpathyContactManagerPriv *priv; - GList *pending = NULL; - - g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - - priv = GET_PRIV (manager); - - g_hash_table_foreach (priv->lists, - (GHFunc) contact_manager_get_local_pending_foreach, - &pending); - - return pending; -} - static void -contact_manager_process_pending (EmpathyContactList *manager, - EmpathyContact *contact, - gboolean accept) +contact_manager_get_pendings_foreach (McAccount *account, + EmpathyTpContactList *list, + GList **contacts) { - EmpathyContactManagerPriv *priv; - EmpathyContactList *list; - McAccount *account; - - g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); - g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - - priv = GET_PRIV (manager); - - account = empathy_contact_get_account (contact); - list = g_hash_table_lookup (priv->lists, account); - - if (list) { - empathy_contact_list_process_pending (list, contact, accept); - } -} - -EmpathyTpContactList * -empathy_contact_manager_get_list (EmpathyContactManager *manager, - McAccount *account) -{ - EmpathyContactManagerPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); - - priv = GET_PRIV (manager); + GList *l; - return g_hash_table_lookup (priv->lists, account); + l = empathy_contact_list_get_pendings (EMPATHY_CONTACT_LIST (list)); + *contacts = g_list_concat (*contacts, l); } -EmpathyContact * -empathy_contact_manager_get_user (EmpathyContactManager *manager, - McAccount *account) +static GList * +contact_manager_get_pendings (EmpathyContactList *manager) { - EmpathyContactManagerPriv *priv; - EmpathyTpContactList *list; + EmpathyContactManagerPriv *priv = GET_PRIV (manager); + GList *contacts = NULL; g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); - - priv = GET_PRIV (manager); - list = g_hash_table_lookup (priv->lists, account); - - if (!list) { - return NULL; - } + g_hash_table_foreach (priv->lists, + (GHFunc) contact_manager_get_pendings_foreach, + &contacts); - return empathy_tp_contact_list_get_user (list); + return contacts; } -EmpathyContact * -empathy_contact_manager_create (EmpathyContactManager *manager, - McAccount *account, - const gchar *id) +static void +contact_manager_get_all_groups_foreach (McAccount *account, + EmpathyTpContactList *list, + GList **all_groups) { - EmpathyContactManagerPriv *priv; - EmpathyTpContactList *list; - - g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); - g_return_val_if_fail (id != NULL, NULL); - - priv = GET_PRIV (manager); + GList *groups, *l; - list = g_hash_table_lookup (priv->lists, account); - - if (!list) { - return NULL; + groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (list)); + for (l = groups; l; l = l->next) { + if (!g_list_find_custom (*all_groups, + l->data, + (GCompareFunc) strcmp)) { + *all_groups = g_list_prepend (*all_groups, l->data); + } else { + g_free (l->data); + } } - return empathy_tp_contact_list_get_from_id (list, id); -} - -void -empathy_contact_manager_rename_group (EmpathyContactManager *manager, - const gchar *old_group, - const gchar *new_group) -{ - EmpathyContactManagerPriv *priv; - ContactManagerRenameGroupData data; - - g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); - g_return_if_fail (old_group != NULL); - g_return_if_fail (new_group != NULL); - - priv = GET_PRIV (manager); - - data.old_group = old_group; - data.new_group = new_group; - - g_hash_table_foreach (priv->lists, - (GHFunc) contact_manager_rename_group_foreach, - &data); + g_list_free (groups); } -GList * -empathy_contact_manager_get_groups (EmpathyContactManager *manager) +static GList * +contact_manager_get_all_groups (EmpathyContactList *manager) { - EmpathyContactManagerPriv *priv; + EmpathyContactManagerPriv *priv = GET_PRIV (manager); GList *groups = NULL; g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - priv = GET_PRIV (manager); - g_hash_table_foreach (priv->lists, - (GHFunc) contact_manager_get_groups_foreach, + (GHFunc) contact_manager_get_all_groups_foreach, &groups); return groups; } -static void -contact_manager_setup_foreach (McAccount *account, - EmpathyTpContactList *list, - EmpathyContactManager *manager) +static GList * +contact_manager_get_groups (EmpathyContactList *manager, + EmpathyContact *contact) { - empathy_contact_list_setup (EMPATHY_CONTACT_LIST (list)); -} + EmpathyContactManagerPriv *priv = GET_PRIV (manager); + EmpathyContactList *list; + McAccount *account; -static gboolean -contact_manager_find_foreach (McAccount *account, - EmpathyTpContactList *list, - ContactManagerFindData *data) -{ - data->contact = empathy_contact_list_find (EMPATHY_CONTACT_LIST (list), - data->id); + g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); + + account = empathy_contact_get_account (contact); + list = g_hash_table_lookup (priv->lists, account); - if (data->contact) { - return TRUE; + if (list) { + return empathy_contact_list_get_groups (list, contact); } - return FALSE; + return NULL; } static void -contact_manager_add_account (EmpathyContactManager *manager, - McAccount *account) +contact_manager_add_to_group (EmpathyContactList *manager, + EmpathyContact *contact, + const gchar *group) { - EmpathyContactManagerPriv *priv; - EmpathyTpContactList *list; - - priv = GET_PRIV (manager); - - if (g_hash_table_lookup (priv->lists, account)) { - return; - } - - empathy_debug (DEBUG_DOMAIN, "Adding new account: %s", - mc_account_get_display_name (account)); - - list = empathy_tp_contact_list_new (account); - if (!list) { - return; - } + EmpathyContactManagerPriv *priv = GET_PRIV (manager); + EmpathyContactList *list; + McAccount *account; - g_hash_table_insert (priv->lists, g_object_ref (account), list); + g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); - /* Connect signals */ - g_signal_connect (list, "contact-added", - G_CALLBACK (contact_manager_added_cb), - manager); - g_signal_connect (list, "contact-removed", - G_CALLBACK (contact_manager_removed_cb), - manager); - g_signal_connect (list, "local-pending", - G_CALLBACK (contact_manager_local_pending_cb), - manager); - g_signal_connect (list, "destroy", - G_CALLBACK (contact_manager_destroy_cb), - manager); + account = empathy_contact_get_account (contact); + list = g_hash_table_lookup (priv->lists, account); - if (priv->setup) { - empathy_contact_list_setup (EMPATHY_CONTACT_LIST (list)); + if (list) { + empathy_contact_list_add_to_group (list, contact, group); } } static void -contact_manager_added_cb (EmpathyTpContactList *list, - EmpathyContact *contact, - EmpathyContactManager *manager) +contact_manager_remove_from_group (EmpathyContactList *manager, + EmpathyContact *contact, + const gchar *group) { - g_signal_emit_by_name (manager, "contact-added", contact); -} - -static void -contact_manager_removed_cb (EmpathyTpContactList *list, - EmpathyContact *contact, - EmpathyContactManager *manager) -{ - g_signal_emit_by_name (manager, "contact-removed", contact); -} - -static void -contact_manager_local_pending_cb (EmpathyTpContactList *list, - EmpathyContact *contact, - const gchar *message, - EmpathyContactManager *manager) -{ - g_signal_emit_by_name (manager, "local-pending", contact, message); -} - -static void -contact_manager_destroy_cb (EmpathyTpContactList *list, - EmpathyContactManager *manager) -{ - EmpathyContactManagerPriv *priv; + EmpathyContactManagerPriv *priv = GET_PRIV (manager); + EmpathyContactList *list; McAccount *account; - priv = GET_PRIV (manager); - - account = empathy_tp_contact_list_get_account (list); - - empathy_debug (DEBUG_DOMAIN, "Removing account: %s", - mc_account_get_display_name (account)); + g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); - /* Disconnect signals from the list */ - g_signal_handlers_disconnect_by_func (list, - contact_manager_added_cb, - manager); - g_signal_handlers_disconnect_by_func (list, - contact_manager_removed_cb, - manager); - g_signal_handlers_disconnect_by_func (list, - contact_manager_local_pending_cb, - manager); - g_signal_handlers_disconnect_by_func (list, - contact_manager_destroy_cb, - manager); + account = empathy_contact_get_account (contact); + list = g_hash_table_lookup (priv->lists, account); - g_hash_table_remove (priv->lists, account); + if (list) { + empathy_contact_list_remove_from_group (list, contact, group); + } } -static void -contact_manager_rename_group_foreach (McAccount *account, - EmpathyTpContactList *list, - ContactManagerRenameGroupData *data) -{ - empathy_tp_contact_list_rename_group (list, - data->old_group, - data->new_group); -} +typedef struct { + const gchar *old_group; + const gchar *new_group; +} RenameGroupData; static void -contact_manager_get_groups_foreach (McAccount *account, - EmpathyTpContactList *list, - GList **all_groups) +contact_manager_rename_group_foreach (McAccount *account, + EmpathyTpContactList *list, + RenameGroupData *data) { - GList *groups, *l; - - groups = empathy_tp_contact_list_get_groups (list); - for (l = groups; l; l = l->next) { - if (!g_list_find_custom (*all_groups, - l->data, - (GCompareFunc) strcmp)) { - *all_groups = g_list_append (*all_groups, - g_strdup (l->data)); - } - g_free (l->data); - } - - g_list_free (groups); + empathy_contact_list_rename_group (EMPATHY_CONTACT_LIST (list), + data->old_group, + data->new_group); } static void -contact_manager_get_members_foreach (McAccount *account, - EmpathyTpContactList *list, - GList **contacts) +contact_manager_rename_group (EmpathyContactList *manager, + const gchar *old_group, + const gchar *new_group) { - GList *l; - - l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list)); - *contacts = g_list_concat (*contacts, l); -} + EmpathyContactManagerPriv *priv = GET_PRIV (manager); + RenameGroupData data; -static void -contact_manager_get_local_pending_foreach (McAccount *account, - EmpathyTpContactList *list, - GList **contacts) -{ - GList *l; + g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); - l = empathy_contact_list_get_local_pending (EMPATHY_CONTACT_LIST (list)); - *contacts = g_list_concat (*contacts, l); + data.old_group = old_group; + data.new_group = new_group; + g_hash_table_foreach (priv->lists, + (GHFunc) contact_manager_rename_group_foreach, + &data); } static void -contact_manager_status_changed_cb (MissionControl *mc, - TelepathyConnectionStatus status, - McPresence presence, - TelepathyConnectionStatusReason reason, - const gchar *unique_name, - EmpathyContactManager *manager) +contact_manager_iface_init (EmpathyContactListIface *iface) { - EmpathyContactManagerPriv *priv; - McAccount *account; - - priv = GET_PRIV (manager); - - if (status != TP_CONN_STATUS_CONNECTED) { - /* We only care about newly connected accounts */ - return; - } - - account = mc_account_lookup (unique_name); - contact_manager_add_account (manager, account); - - g_object_unref (account); + iface->add = contact_manager_add; + iface->remove = contact_manager_remove; + iface->get_members = contact_manager_get_members; + iface->get_pendings = contact_manager_get_pendings; + iface->get_all_groups = contact_manager_get_all_groups; + iface->get_groups = contact_manager_get_groups; + iface->add_to_group = contact_manager_add_to_group; + iface->remove_from_group = contact_manager_remove_from_group; + iface->rename_group = contact_manager_rename_group; } diff --git a/libempathy/empathy-contact-manager.h b/libempathy/empathy-contact-manager.h index 768301ae7..41144cf43 100644 --- a/libempathy/empathy-contact-manager.h +++ b/libempathy/empathy-contact-manager.h @@ -51,19 +51,10 @@ struct _EmpathyContactManagerClass { GObjectClass parent_class; }; -GType empathy_contact_manager_get_type (void) G_GNUC_CONST; -EmpathyContactManager *empathy_contact_manager_new (void); -EmpathyTpContactList * empathy_contact_manager_get_list (EmpathyContactManager *manager, - McAccount *account); -EmpathyContact * empathy_contact_manager_get_user (EmpathyContactManager *manager, - McAccount *account); -EmpathyContact * empathy_contact_manager_create (EmpathyContactManager *manager, - McAccount *account, - const gchar *id); -void empathy_contact_manager_rename_group (EmpathyContactManager *manager, - const gchar *old_group, - const gchar *new_group); -GList * empathy_contact_manager_get_groups (EmpathyContactManager *manager); +GType empathy_contact_manager_get_type (void) G_GNUC_CONST; +EmpathyContactManager *empathy_contact_manager_new (void); +EmpathyTpContactList * empathy_contact_manager_get_list (EmpathyContactManager *manager, + McAccount *account); G_END_DECLS diff --git a/libempathy/empathy-contact.c b/libempathy/empathy-contact.c index d3935ad6c..505e190c0 100644 --- a/libempathy/empathy-contact.c +++ b/libempathy/empathy-contact.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Copyright (C) 2004-2007 Imendio AB + * Copyright (C) 2004 Imendio AB + * Copyright (C) 2007 Collabora Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -19,6 +20,7 @@ * * Authors: Mikael Hallendal <micke@imendio.com> * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> */ #include "config.h" @@ -41,26 +43,26 @@ typedef struct _EmpathyContactPriv EmpathyContactPriv; struct _EmpathyContactPriv { gchar *id; gchar *name; - EmpathyAvatar *avatar; + EmpathyAvatar *avatar; McAccount *account; - EmpathyPresence *presence; - GList *groups; - EmpathySubscription subscription; + EmpathyPresence *presence; guint handle; gboolean is_user; }; -static void contact_class_init (EmpathyContactClass *class); -static void contact_init (EmpathyContact *contact); -static void contact_finalize (GObject *object); -static void contact_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void contact_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); +static void empathy_contact_class_init (EmpathyContactClass *class); +static void empathy_contact_init (EmpathyContact *contact); +static void contact_finalize (GObject *object); +static void contact_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void contact_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +G_DEFINE_TYPE (EmpathyContact, empathy_contact, G_TYPE_OBJECT); enum { PROP_0, @@ -75,41 +77,12 @@ enum { PROP_IS_USER }; -static gpointer parent_class = NULL; - -GType -empathy_contact_get_gtype (void) -{ - static GType type = 0; - - if (!type) { - static const GTypeInfo info = { - sizeof (EmpathyContactClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) contact_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (EmpathyContact), - 0, /* n_preallocs */ - (GInstanceInitFunc) contact_init - }; - - type = g_type_register_static (G_TYPE_OBJECT, - "EmpathyContact", - &info, 0); - } - - return type; -} - static void -contact_class_init (EmpathyContactClass *class) +empathy_contact_class_init (EmpathyContactClass *class) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (class); - parent_class = g_type_class_peek_parent (class); object_class->finalize = contact_finalize; object_class->get_property = contact_get_property; @@ -156,23 +129,6 @@ contact_class_init (EmpathyContactClass *class) G_PARAM_READWRITE)); g_object_class_install_property (object_class, - PROP_GROUPS, - g_param_spec_pointer ("groups", - "Contact groups", - "Groups of contact", - G_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_SUBSCRIPTION, - g_param_spec_flags ("subscription", - "Contact Subscription", - "The subscription status of the contact", - EMPATHY_TYPE_SUBSCRIPTION, - EMPATHY_SUBSCRIPTION_NONE, - G_PARAM_READWRITE)); - - - g_object_class_install_property (object_class, PROP_HANDLE, g_param_spec_uint ("handle", "Contact Handle", @@ -193,7 +149,7 @@ contact_class_init (EmpathyContactClass *class) } static void -contact_init (EmpathyContact *contact) +empathy_contact_init (EmpathyContact *contact) { } @@ -217,16 +173,11 @@ contact_finalize (GObject *object) g_object_unref (priv->presence); } - if (priv->groups) { - g_list_foreach (priv->groups, (GFunc) g_free, NULL); - g_list_free (priv->groups); - } - if (priv->account) { g_object_unref (priv->account); } - (G_OBJECT_CLASS (parent_class)->finalize) (object); + G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object); } static void @@ -257,12 +208,6 @@ contact_get_property (GObject *object, case PROP_PRESENCE: g_value_set_object (value, priv->presence); break; - case PROP_GROUPS: - g_value_set_pointer (value, priv->groups); - break; - case PROP_SUBSCRIPTION: - g_value_set_flags (value, priv->subscription); - break; case PROP_HANDLE: g_value_set_uint (value, priv->handle); break; @@ -306,14 +251,6 @@ contact_set_property (GObject *object, empathy_contact_set_presence (EMPATHY_CONTACT (object), EMPATHY_PRESENCE (g_value_get_object (value))); break; - case PROP_GROUPS: - empathy_contact_set_groups (EMPATHY_CONTACT (object), - g_value_get_pointer (value)); - break; - case PROP_SUBSCRIPTION: - empathy_contact_set_subscription (EMPATHY_CONTACT (object), - g_value_get_flags (value)); - break; case PROP_HANDLE: empathy_contact_set_handle (EMPATHY_CONTACT (object), g_value_get_uint (value)); @@ -338,8 +275,8 @@ empathy_contact_new (McAccount *account) EmpathyContact * empathy_contact_new_full (McAccount *account, - const gchar *id, - const gchar *name) + const gchar *id, + const gchar *name) { return g_object_new (EMPATHY_TYPE_CONTACT, "account", account, @@ -364,107 +301,6 @@ empathy_contact_get_id (EmpathyContact *contact) return ""; } -const gchar * -empathy_contact_get_name (EmpathyContact *contact) -{ - EmpathyContactPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), ""); - - priv = GET_PRIV (contact); - - if (G_STR_EMPTY (priv->name)) { - return empathy_contact_get_id (contact); - } - - return priv->name; -} - -EmpathyAvatar * -empathy_contact_get_avatar (EmpathyContact *contact) -{ - EmpathyContactPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); - - priv = GET_PRIV (contact); - - return priv->avatar; -} - -McAccount * -empathy_contact_get_account (EmpathyContact *contact) -{ - EmpathyContactPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); - - priv = GET_PRIV (contact); - - return priv->account; -} - -EmpathyPresence * -empathy_contact_get_presence (EmpathyContact *contact) -{ - EmpathyContactPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); - - priv = GET_PRIV (contact); - - return priv->presence; -} - -GList * -empathy_contact_get_groups (EmpathyContact *contact) -{ - EmpathyContactPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); - - priv = GET_PRIV (contact); - - return priv->groups; -} - -EmpathySubscription -empathy_contact_get_subscription (EmpathyContact *contact) -{ - EmpathyContactPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), - EMPATHY_SUBSCRIPTION_NONE); - - priv = GET_PRIV (contact); - - return priv->subscription; -} - -guint -empathy_contact_get_handle (EmpathyContact *contact) -{ - EmpathyContactPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0); - - priv = GET_PRIV (contact); - - return priv->handle; -} - -gboolean -empathy_contact_is_user (EmpathyContact *contact) -{ - EmpathyContactPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE); - - priv = GET_PRIV (contact); - - return priv->is_user; -} - void empathy_contact_set_id (EmpathyContact *contact, const gchar *id) @@ -486,6 +322,22 @@ empathy_contact_set_id (EmpathyContact *contact, g_object_notify (G_OBJECT (contact), "id"); } +const gchar * +empathy_contact_get_name (EmpathyContact *contact) +{ + EmpathyContactPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), ""); + + priv = GET_PRIV (contact); + + if (G_STR_EMPTY (priv->name)) { + return empathy_contact_get_id (contact); + } + + return priv->name; +} + void empathy_contact_set_name (EmpathyContact *contact, const gchar *name) @@ -507,6 +359,18 @@ empathy_contact_set_name (EmpathyContact *contact, g_object_notify (G_OBJECT (contact), "name"); } +EmpathyAvatar * +empathy_contact_get_avatar (EmpathyContact *contact) +{ + EmpathyContactPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); + + priv = GET_PRIV (contact); + + return priv->avatar; +} + void empathy_contact_set_avatar (EmpathyContact *contact, EmpathyAvatar *avatar) @@ -533,9 +397,21 @@ empathy_contact_set_avatar (EmpathyContact *contact, g_object_notify (G_OBJECT (contact), "avatar"); } +McAccount * +empathy_contact_get_account (EmpathyContact *contact) +{ + EmpathyContactPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); + + priv = GET_PRIV (contact); + + return priv->account; +} + void empathy_contact_set_account (EmpathyContact *contact, - McAccount *account) + McAccount *account) { EmpathyContactPriv *priv; @@ -556,9 +432,21 @@ empathy_contact_set_account (EmpathyContact *contact, g_object_notify (G_OBJECT (contact), "account"); } +EmpathyPresence * +empathy_contact_get_presence (EmpathyContact *contact) +{ + EmpathyContactPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); + + priv = GET_PRIV (contact); + + return priv->presence; +} + void empathy_contact_set_presence (EmpathyContact *contact, - EmpathyPresence *presence) + EmpathyPresence *presence) { EmpathyContactPriv *priv; @@ -582,34 +470,21 @@ empathy_contact_set_presence (EmpathyContact *contact, g_object_notify (G_OBJECT (contact), "presence"); } -void -empathy_contact_set_groups (EmpathyContact *contact, - GList *groups) +guint +empathy_contact_get_handle (EmpathyContact *contact) { EmpathyContactPriv *priv; - GList *old_groups, *l; - g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0); priv = GET_PRIV (contact); - old_groups = priv->groups; - priv->groups = NULL; - - for (l = groups; l; l = l->next) { - priv->groups = g_list_append (priv->groups, - g_strdup (l->data)); - } - - g_list_foreach (old_groups, (GFunc) g_free, NULL); - g_list_free (old_groups); - - g_object_notify (G_OBJECT (contact), "groups"); + return priv->handle; } void -empathy_contact_set_subscription (EmpathyContact *contact, - EmpathySubscription subscription) +empathy_contact_set_handle (EmpathyContact *contact, + guint handle) { EmpathyContactPriv *priv; @@ -617,32 +492,25 @@ empathy_contact_set_subscription (EmpathyContact *contact, priv = GET_PRIV (contact); - if (priv->subscription == subscription) { + if (priv->handle == handle) { return; } - priv->subscription = subscription; + priv->handle = handle; - g_object_notify (G_OBJECT (contact), "subscription"); + g_object_notify (G_OBJECT (contact), "handle"); } -void -empathy_contact_set_handle (EmpathyContact *contact, - guint handle) +gboolean +empathy_contact_is_user (EmpathyContact *contact) { EmpathyContactPriv *priv; - g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE); priv = GET_PRIV (contact); - if (priv->handle == handle) { - return; - } - - priv->handle = handle; - - g_object_notify (G_OBJECT (contact), "handle"); + return priv->is_user; } void @@ -664,43 +532,6 @@ empathy_contact_set_is_user (EmpathyContact *contact, g_object_notify (G_OBJECT (contact), "is-user"); } -void -empathy_contact_add_group (EmpathyContact *contact, - const gchar *group) -{ - EmpathyContactPriv *priv; - - g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - g_return_if_fail (group != NULL); - - priv = GET_PRIV (contact); - - if (!g_list_find_custom (priv->groups, group, (GCompareFunc) strcmp)) { - priv->groups = g_list_prepend (priv->groups, g_strdup (group)); - g_object_notify (G_OBJECT (contact), "groups"); - } -} - -void -empathy_contact_remove_group (EmpathyContact *contact, - const gchar *group) -{ - EmpathyContactPriv *priv; - GList *l; - - g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - g_return_if_fail (group != NULL); - - priv = GET_PRIV (contact); - - l = g_list_find_custom (priv->groups, group, (GCompareFunc) strcmp); - if (l) { - g_free (l->data); - priv->groups = g_list_delete_link (priv->groups, l); - g_object_notify (G_OBJECT (contact), "groups"); - } -} - gboolean empathy_contact_is_online (EmpathyContact *contact) { @@ -717,24 +548,6 @@ empathy_contact_is_online (EmpathyContact *contact) return (empathy_presence_get_state (priv->presence) > MC_PRESENCE_OFFLINE); } -gboolean -empathy_contact_is_in_group (EmpathyContact *contact, - const gchar *group) -{ - EmpathyContactPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE); - g_return_val_if_fail (!G_STR_EMPTY (group), FALSE); - - priv = GET_PRIV (contact); - - if (g_list_find_custom (priv->groups, group, (GCompareFunc) strcmp)) { - return TRUE; - } - - return FALSE; -} - const gchar * empathy_contact_get_status (EmpathyContact *contact) { diff --git a/libempathy/empathy-contact.h b/libempathy/empathy-contact.h index b32d451bb..87a26f0dd 100644 --- a/libempathy/empathy-contact.h +++ b/libempathy/empathy-contact.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2004 Imendio AB + * Copyright (C) 2007 Collabora Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -16,6 +17,10 @@ * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> */ #ifndef __EMPATHY_CONTACT_H__ @@ -30,7 +35,7 @@ G_BEGIN_DECLS -#define EMPATHY_TYPE_CONTACT (empathy_contact_get_gtype ()) +#define EMPATHY_TYPE_CONTACT (empathy_contact_get_type ()) #define EMPATHY_CONTACT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CONTACT, EmpathyContact)) #define EMPATHY_CONTACT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_CONTACT, EmpathyContactClass)) #define EMPATHY_IS_CONTACT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CONTACT)) @@ -48,57 +53,37 @@ struct _EmpathyContactClass { GObjectClass parent_class; }; -typedef enum { - EMPATHY_SUBSCRIPTION_NONE = 0, - EMPATHY_SUBSCRIPTION_TO = 1 << 0, /* We send our presence to that contact */ - EMPATHY_SUBSCRIPTION_FROM = 1 << 1, /* That contact sends his presence to us */ - EMPATHY_SUBSCRIPTION_BOTH = EMPATHY_SUBSCRIPTION_TO | EMPATHY_SUBSCRIPTION_FROM -} EmpathySubscription; - -GType empathy_contact_get_gtype (void) G_GNUC_CONST; - -EmpathyContact * empathy_contact_new (McAccount *account); -EmpathyContact * empathy_contact_new_full (McAccount *account, - const gchar *id, - const gchar *name); -const gchar * empathy_contact_get_id (EmpathyContact *contact); -const gchar * empathy_contact_get_name (EmpathyContact *contact); -EmpathyAvatar * empathy_contact_get_avatar (EmpathyContact *contact); -McAccount * empathy_contact_get_account (EmpathyContact *contact); -EmpathyPresence * empathy_contact_get_presence (EmpathyContact *contact); -GList * empathy_contact_get_groups (EmpathyContact *contact); -EmpathySubscription empathy_contact_get_subscription (EmpathyContact *contact); -guint empathy_contact_get_handle (EmpathyContact *contact); -gboolean empathy_contact_is_user (EmpathyContact *contact); -void empathy_contact_set_id (EmpathyContact *contact, - const gchar *id); -void empathy_contact_set_name (EmpathyContact *contact, - const gchar *name); -void empathy_contact_set_avatar (EmpathyContact *contact, - EmpathyAvatar *avatar); -void empathy_contact_set_account (EmpathyContact *contact, - McAccount *account); -void empathy_contact_set_presence (EmpathyContact *contact, - EmpathyPresence *presence); -void empathy_contact_set_groups (EmpathyContact *contact, - GList *categories); -void empathy_contact_set_subscription (EmpathyContact *contact, - EmpathySubscription subscription); -void empathy_contact_set_handle (EmpathyContact *contact, - guint handle); -void empathy_contact_set_is_user (EmpathyContact *contact, - gboolean is_user); -void empathy_contact_add_group (EmpathyContact *contact, - const gchar *group); -void empathy_contact_remove_group (EmpathyContact *contact, - const gchar *group); -gboolean empathy_contact_is_online (EmpathyContact *contact); -gboolean empathy_contact_is_in_group (EmpathyContact *contact, - const gchar *group); -const gchar * empathy_contact_get_status (EmpathyContact *contact); -gboolean empathy_contact_equal (gconstpointer v1, - gconstpointer v2); -guint empathy_contact_hash (gconstpointer key); +GType empathy_contact_get_type (void) G_GNUC_CONST; +EmpathyContact * empathy_contact_new (McAccount *account); +EmpathyContact * empathy_contact_new_full (McAccount *account, + const gchar *id, + const gchar *name); +const gchar * empathy_contact_get_id (EmpathyContact *contact); +void empathy_contact_set_id (EmpathyContact *contact, + const gchar *id); +const gchar * empathy_contact_get_name (EmpathyContact *contact); +void empathy_contact_set_name (EmpathyContact *contact, + const gchar *name); +EmpathyAvatar * empathy_contact_get_avatar (EmpathyContact *contact); +void empathy_contact_set_avatar (EmpathyContact *contact, + EmpathyAvatar *avatar); +McAccount * empathy_contact_get_account (EmpathyContact *contact); +void empathy_contact_set_account (EmpathyContact *contact, + McAccount *account); +EmpathyPresence * empathy_contact_get_presence (EmpathyContact *contact); +void empathy_contact_set_presence (EmpathyContact *contact, + EmpathyPresence *presence); +guint empathy_contact_get_handle (EmpathyContact *contact); +void empathy_contact_set_handle (EmpathyContact *contact, + guint handle); +gboolean empathy_contact_is_user (EmpathyContact *contact); +void empathy_contact_set_is_user (EmpathyContact *contact, + gboolean is_user); +gboolean empathy_contact_is_online (EmpathyContact *contact); +const gchar * empathy_contact_get_status (EmpathyContact *contact); +gboolean empathy_contact_equal (gconstpointer v1, + gconstpointer v2); +guint empathy_contact_hash (gconstpointer key); G_END_DECLS diff --git a/libempathy/empathy-log-manager.c b/libempathy/empathy-log-manager.c index e234ec521..e47f50fb8 100644 --- a/libempathy/empathy-log-manager.c +++ b/libempathy/empathy-log-manager.c @@ -634,24 +634,22 @@ log_manager_get_dir (EmpathyLogManager *manager, { const gchar *account_id; gchar *basedir; - gchar *str; account_id = mc_account_get_unique_name (account); - basedir = - str = g_build_path (G_DIR_SEPARATOR_S, - log_manager_get_basedir (manager), - account_id, - chat_id, - NULL); if (chatroom) { basedir = g_build_path (G_DIR_SEPARATOR_S, - str, + log_manager_get_basedir (manager), + account_id, LOG_DIR_CHATROOMS, + chat_id, NULL); - g_free (str); } else { - basedir = str; + basedir = g_build_path (G_DIR_SEPARATOR_S, + log_manager_get_basedir (manager), + account_id, + chat_id, + NULL); } return basedir; diff --git a/libempathy/empathy-marshal.list b/libempathy/empathy-marshal.list index 3b36b7be0..383bfbbed 100644 --- a/libempathy/empathy-marshal.list +++ b/libempathy/empathy-marshal.list @@ -1,10 +1,14 @@ VOID:OBJECT,UINT -VOID:OBJECT,OBJECT -VOID:OBJECT,OBJECT,UINT VOID:OBJECT,BOOLEAN -VOID:OBJECT,STRING,STRING +VOID:OBJECT,POINTER VOID:OBJECT,STRING -VOID:POINTER,UINT,UINT,STRING +VOID:OBJECT,STRING,STRING +VOID:OBJECT,STRING,BOOLEAN +VOID:OBJECT,OBJECT +VOID:OBJECT,OBJECT,UINT +VOID:OBJECT,OBJECT,UINT,STRING +VOID:OBJECT,OBJECT,UINT,STRING,BOOLEAN +VOID:OBJECT,OBJECT,STRING VOID:INT,STRING VOID:UINT,BOOLEAN diff --git a/libempathy/empathy-tp-chat.c b/libempathy/empathy-tp-chat.c index 077afe36e..fc943f0c6 100644 --- a/libempathy/empathy-tp-chat.c +++ b/libempathy/empathy-tp-chat.c @@ -31,8 +31,7 @@ #include <libtelepathy/tp-props-iface.h> #include "empathy-tp-chat.h" -#include "empathy-contact-manager.h" -#include "empathy-tp-contact-list.h" +#include "empathy-contact-factory.h" #include "empathy-marshal.h" #include "empathy-debug.h" #include "empathy-time.h" @@ -44,8 +43,8 @@ #define DEBUG_DOMAIN "TpChat" struct _EmpathyTpChatPriv { - EmpathyTpContactList *list; - EmpathyContactManager *manager; + EmpathyContactFactory *factory; + EmpathyContact *user; McAccount *account; gchar *id; MissionControl *mc; @@ -363,11 +362,11 @@ tp_chat_finalize (GObject *object) g_object_unref (priv->tp_chan); } - if (priv->manager) { - g_object_unref (priv->manager); + if (priv->factory) { + g_object_unref (priv->factory); } - if (priv->list) { - g_object_unref (priv->list); + if (priv->user) { + g_object_unref (priv->user); } if (priv->account) { g_object_unref (priv->account); @@ -392,10 +391,9 @@ tp_chat_constructor (GType type, priv = GET_PRIV (chat); - priv->manager = empathy_contact_manager_new (); - priv->list = empathy_contact_manager_get_list (priv->manager, priv->account); + priv->factory = empathy_contact_factory_new (); + priv->user = empathy_contact_factory_get_user (priv->factory, priv->account); priv->mc = empathy_mission_control_new (); - g_object_ref (priv->list); priv->text_iface = tp_chan_get_interface (priv->tp_chan, TELEPATHY_CHAN_IFACE_TEXT_QUARK); @@ -750,12 +748,10 @@ empathy_tp_chat_get_id (EmpathyTpChat *chat) priv = GET_PRIV (chat); - if (priv->id) { - return priv->id; + if (!priv->id) { + priv->id = empathy_inspect_channel (priv->account, priv->tp_chan); } - priv->id = empathy_inspect_channel (priv->account, priv->tp_chan); - return priv->id; } @@ -885,7 +881,9 @@ tp_chat_state_changed_cb (DBusGProxy *chat_state_iface, priv = GET_PRIV (chat); - contact = empathy_tp_contact_list_get_from_handle (priv->list, handle); + 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), @@ -906,22 +904,21 @@ tp_chat_build_message (EmpathyTpChat *chat, EmpathyTpChatPriv *priv; EmpathyMessage *message; EmpathyContact *sender; - EmpathyContact *receiver; priv = GET_PRIV (chat); - receiver = empathy_tp_contact_list_get_user (priv->list); if (from_handle == 0) { - sender = g_object_ref (receiver); + sender = g_object_ref (priv->user); } else { - sender = empathy_tp_contact_list_get_from_handle (priv->list, + sender = empathy_contact_factory_get_from_handle (priv->factory, + priv->account, from_handle); } message = empathy_message_new (message_body); empathy_message_set_type (message, type); empathy_message_set_sender (message, sender); - empathy_message_set_receiver (message, receiver); + empathy_message_set_receiver (message, priv->user); empathy_message_set_timestamp (message, (EmpathyTime) timestamp); g_object_unref (sender); diff --git a/libempathy/empathy-tp-chatroom.c b/libempathy/empathy-tp-chatroom.c index 32716865d..72f083942 100644 --- a/libempathy/empathy-tp-chatroom.c +++ b/libempathy/empathy-tp-chatroom.c @@ -23,9 +23,8 @@ #include <config.h> #include "empathy-tp-chatroom.h" -#include "empathy-tp-contact-list.h" #include "empathy-contact-list.h" -#include "empathy-contact-manager.h" +#include "empathy-contact-factory.h" #include "empathy-tp-group.h" #include "empathy-utils.h" #include "empathy-debug.h" @@ -36,8 +35,7 @@ #define DEBUG_DOMAIN "TpChatroom" struct _EmpathyTpChatroomPriv { - EmpathyContactManager *manager; - EmpathyTpContactList *list; + EmpathyContactFactory *factory; EmpathyTpGroup *group; gboolean is_invited; @@ -49,21 +47,18 @@ static void empathy_tp_chatroom_class_init (EmpathyTpChatroomClass * static void tp_chatroom_iface_init (EmpathyContactListIface *iface); static void empathy_tp_chatroom_init (EmpathyTpChatroom *chatroom); static void tp_chatroom_finalize (GObject *object); -static void tp_chatroom_members_added_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, +static void tp_chatroom_member_added_cb (EmpathyTpGroup *group, + EmpathyContact *contact, + EmpathyContact *actor, guint reason, const gchar *message, - EmpathyTpChatroom *list); -static void tp_chatroom_members_removed_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, + EmpathyTpChatroom *chatroom); +static void tp_chatroom_member_removed_cb (EmpathyTpGroup *group, + EmpathyContact *contact, + EmpathyContact *actor, guint reason, const gchar *message, - EmpathyTpChatroom *list); -static void tp_chatroom_setup (EmpathyContactList *list); -static EmpathyContact * tp_chatroom_find (EmpathyContactList *list, - const gchar *id); + EmpathyTpChatroom *chatroom); static void tp_chatroom_add (EmpathyContactList *list, EmpathyContact *contact, const gchar *message); @@ -89,8 +84,6 @@ empathy_tp_chatroom_class_init (EmpathyTpChatroomClass *klass) static void tp_chatroom_iface_init (EmpathyContactListIface *iface) { - iface->setup = tp_chatroom_setup; - iface->find = tp_chatroom_find; iface->add = tp_chatroom_add; iface->remove = tp_chatroom_remove; iface->get_members = tp_chatroom_get_members; @@ -111,7 +104,7 @@ tp_chatroom_finalize (GObject *object) priv = GET_PRIV (chatroom); g_object_unref (priv->group); - g_object_unref (priv->manager); + g_object_unref (priv->factory); if (priv->invitor) { g_object_unref (priv->invitor); @@ -129,7 +122,7 @@ empathy_tp_chatroom_new (McAccount *account, EmpathyTpChatroomPriv *priv; EmpathyTpChatroom *chatroom; GList *members, *l; - guint self_handle; + EmpathyContact *user; g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL); @@ -141,40 +134,41 @@ empathy_tp_chatroom_new (McAccount *account, priv = GET_PRIV (chatroom); - priv->manager = empathy_contact_manager_new (); - priv->list = empathy_contact_manager_get_list (priv->manager, account); + priv->factory = empathy_contact_factory_new (); priv->group = empathy_tp_group_new (account, tp_chan); - g_signal_connect (priv->group, "members-added", - G_CALLBACK (tp_chatroom_members_added_cb), + g_signal_connect (priv->group, "member-added", + G_CALLBACK (tp_chatroom_member_added_cb), chatroom); - g_signal_connect (priv->group, "members-removed", - G_CALLBACK (tp_chatroom_members_removed_cb), + g_signal_connect (priv->group, "member-removed", + G_CALLBACK (tp_chatroom_member_removed_cb), chatroom); /* Check if we are invited to join the chat */ - self_handle = empathy_tp_group_get_self_handle (priv->group); - members = empathy_tp_group_get_local_pending_members_with_info (priv->group); + user = empathy_tp_group_get_self_contact (priv->group); + members = empathy_tp_group_get_local_pendings (priv->group); for (l = members; l; l = l->next) { - EmpathyTpGroupInfo *info; + EmpathyPendingInfo *info; info = l->data; - if (info->member != self_handle) { + if (!empathy_contact_equal (user, info->member)) { continue; } - priv->invitor = empathy_tp_contact_list_get_from_handle (priv->list, - info->actor); + priv->invitor = g_object_ref (info->actor); priv->invit_message = g_strdup (info->message); priv->is_invited = TRUE; - empathy_debug (DEBUG_DOMAIN, "We are invited to join by %s: %s", - empathy_contact_get_name (priv->invitor), - priv->invit_message); + empathy_debug (DEBUG_DOMAIN, "We are invited to join by %s (%d): %s", + empathy_contact_get_id (priv->invitor), + empathy_contact_get_handle (priv->invitor), + priv->invit_message); } - empathy_tp_group_info_list_free (members); + g_list_foreach (members, (GFunc) empathy_pending_info_free, NULL); + g_list_free (members); + g_object_unref (user); return chatroom; } @@ -204,7 +198,7 @@ void empathy_tp_chatroom_accept_invitation (EmpathyTpChatroom *chatroom) { EmpathyTpChatroomPriv *priv; - guint self_handle; + EmpathyContact *user; g_return_if_fail (EMPATHY_IS_TP_CHATROOM (chatroom)); @@ -220,80 +214,42 @@ empathy_tp_chatroom_accept_invitation (EmpathyTpChatroom *chatroom) priv->invit_message = NULL; /* Add ourself in the members of the room */ - self_handle = empathy_tp_group_get_self_handle (priv->group); - empathy_tp_group_add_member (priv->group, self_handle, - "Just for fun"); + user = empathy_tp_group_get_self_contact (priv->group); + empathy_tp_group_add_member (priv->group, user, ""); + g_object_unref (user); } void empathy_tp_chatroom_set_topic (EmpathyTpChatroom *chatroom, const gchar *topic) { + /* FIXME: not implemented */ } static void -tp_chatroom_members_added_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpChatroom *chatroom) +tp_chatroom_member_added_cb (EmpathyTpGroup *group, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + const gchar *message, + EmpathyTpChatroom *chatroom) { - EmpathyTpChatroomPriv *priv; - GList *contacts, *l; - - priv = GET_PRIV (chatroom); - - contacts = empathy_tp_contact_list_get_from_handles (priv->list, handles); - for (l = contacts; l; l = l->next) { - EmpathyContact *contact; - - contact = l->data; - - g_signal_emit_by_name (chatroom, "contact-added", contact); - - g_object_unref (contact); - } - g_list_free (contacts); -} - -static void -tp_chatroom_members_removed_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpChatroom *chatroom) -{ - EmpathyTpChatroomPriv *priv; - GList *contacts, *l; - - priv = GET_PRIV (chatroom); - - contacts = empathy_tp_contact_list_get_from_handles (priv->list, handles); - for (l = contacts; l; l = l->next) { - EmpathyContact *contact; - - contact = l->data; - - g_signal_emit_by_name (chatroom, "contact-removed", contact); - - g_object_unref (contact); - } - g_list_free (contacts); + g_signal_emit_by_name (chatroom, "members-changed", + contact, actor, reason, message, + TRUE); } static void -tp_chatroom_setup (EmpathyContactList *list) -{ - /* Nothing to do */ -} - -static EmpathyContact * -tp_chatroom_find (EmpathyContactList *list, - const gchar *id) +tp_chatroom_member_removed_cb (EmpathyTpGroup *group, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + const gchar *message, + EmpathyTpChatroom *chatroom) { - return NULL; + g_signal_emit_by_name (chatroom, "members-changed", + contact, actor, reason, message, + FALSE); } static void @@ -308,9 +264,7 @@ tp_chatroom_add (EmpathyContactList *list, priv = GET_PRIV (list); - empathy_tp_group_add_member (priv->group, - empathy_contact_get_handle (contact), - message); + empathy_tp_group_add_member (priv->group, contact, message); } static void @@ -325,26 +279,18 @@ tp_chatroom_remove (EmpathyContactList *list, priv = GET_PRIV (list); - empathy_tp_group_remove_member (priv->group, - empathy_contact_get_handle (contact), - message); + empathy_tp_group_remove_member (priv->group, contact, message); } static GList * tp_chatroom_get_members (EmpathyContactList *list) { EmpathyTpChatroomPriv *priv; - GArray *members; - GList *contacts; g_return_val_if_fail (EMPATHY_IS_TP_CHATROOM (list), NULL); priv = GET_PRIV (list); - members = empathy_tp_group_get_members (priv->group); - contacts = empathy_tp_contact_list_get_from_handles (priv->list, members); - g_array_free (members, TRUE); - - return contacts; + return empathy_tp_group_get_members (priv->group); } diff --git a/libempathy/empathy-tp-contact-list.c b/libempathy/empathy-tp-contact-list.c index a5c54b74e..16ecad807 100644 --- a/libempathy/empathy-tp-contact-list.c +++ b/libempathy/empathy-tp-contact-list.c @@ -30,9 +30,6 @@ #include <libtelepathy/tp-conn.h> #include <libtelepathy/tp-chan.h> #include <libtelepathy/tp-chan-type-contact-list-gen.h> -#include <libtelepathy/tp-conn-iface-aliasing-gen.h> -#include <libtelepathy/tp-conn-iface-presence-gen.h> -#include <libtelepathy/tp-conn-iface-avatars-gen.h> #include "empathy-tp-contact-list.h" #include "empathy-contact-list.h" @@ -44,906 +41,305 @@ EMPATHY_TYPE_TP_CONTACT_LIST, EmpathyTpContactListPriv)) #define DEBUG_DOMAIN "TpContactList" -#define MAX_AVATAR_REQUESTS 10 struct _EmpathyTpContactListPriv { TpConn *tp_conn; McAccount *account; MissionControl *mc; - EmpathyContact *user_contact; - gboolean setup; const gchar *protocol_group; EmpathyTpGroup *publish; EmpathyTpGroup *subscribe; - - GHashTable *groups; - GHashTable *contacts; GList *members; - GList *local_pending; + GList *pendings; - DBusGProxy *aliasing_iface; - DBusGProxy *avatars_iface; - DBusGProxy *presence_iface; + GList *groups; + GHashTable *contacts_groups; }; typedef enum { TP_CONTACT_LIST_TYPE_PUBLISH, TP_CONTACT_LIST_TYPE_SUBSCRIBE, - TP_CONTACT_LIST_TYPE_UNKNOWN, - TP_CONTACT_LIST_TYPE_COUNT + TP_CONTACT_LIST_TYPE_UNKNOWN } TpContactListType; -typedef struct { - guint handle; - GList *new_groups; -} TpContactListData; - -typedef struct { - EmpathyTpContactList *list; - guint handle; -} TpContactListAvatarRequestData; - -typedef struct { - EmpathyTpContactList *list; - guint *handles; -} TpContactListAliasesRequestData; - -static void empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass); -static void tp_contact_list_iface_init (EmpathyContactListIface *iface); -static void empathy_tp_contact_list_init (EmpathyTpContactList *list); -static void tp_contact_list_finalize (GObject *object); -static void tp_contact_list_finalize_proxies (EmpathyTpContactList *list); -static void tp_contact_list_setup (EmpathyContactList *list); -static EmpathyContact * tp_contact_list_find (EmpathyContactList *list, - const gchar *id); -static void tp_contact_list_add (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message); -static void tp_contact_list_remove (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message); -static GList * tp_contact_list_get_members (EmpathyContactList *list); -static GList * tp_contact_list_get_local_pending (EmpathyContactList *list); -static void tp_contact_list_process_pending (EmpathyContactList *list, - EmpathyContact *contact, - gboolean accept); -static void tp_contact_list_remove_local_pending (EmpathyTpContactList *list, - EmpathyContact *contact); -static void tp_contact_list_contact_removed_foreach (guint handle, - EmpathyContact *contact, - EmpathyTpContactList *list); -static void tp_contact_list_destroy_cb (DBusGProxy *proxy, - EmpathyTpContactList *list); -static gboolean tp_contact_list_find_foreach (guint handle, - EmpathyContact *contact, - gchar *id); -static void tp_contact_list_newchannel_cb (DBusGProxy *proxy, - const gchar *object_path, - const gchar *channel_type, - TelepathyHandleType handle_type, - guint channel_handle, - gboolean suppress_handle, - EmpathyTpContactList *list); -static TpContactListType tp_contact_list_get_type (EmpathyTpContactList *list, - EmpathyTpGroup *group); -static void tp_contact_list_added_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list); -static void tp_contact_list_removed_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list); -static void tp_contact_list_pending_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list); -static void tp_contact_list_groups_updated_cb (EmpathyContact *contact, - GParamSpec *param, - EmpathyTpContactList *list); -static void tp_contact_list_name_updated_cb (EmpathyContact *contact, - GParamSpec *param, - EmpathyTpContactList *list); -static void tp_contact_list_update_groups_foreach (gchar *object_path, - EmpathyTpGroup *group, - TpContactListData *data); -static EmpathyTpGroup * tp_contact_list_get_group (EmpathyTpContactList *list, - const gchar *name); -static gboolean tp_contact_list_find_group (gchar *key, - EmpathyTpGroup *group, - gchar *group_name); -static void tp_contact_list_get_groups_foreach (gchar *key, - EmpathyTpGroup *group, - GList **groups); -static void tp_contact_list_group_channel_closed_cb (TpChan *channel, - EmpathyTpContactList *list); -static void tp_contact_list_group_members_added_cb (EmpathyTpGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list); -static void tp_contact_list_group_members_removed_cb (EmpathyTpGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list); -static void tp_contact_list_get_info (EmpathyTpContactList *list, - GArray *handles); -static void tp_contact_list_request_avatar (EmpathyTpContactList *list, - guint handle); -static void tp_contact_list_start_avatar_requests (void); -static void tp_contact_list_avatar_update_cb (DBusGProxy *proxy, - guint handle, - gchar *new_token, - EmpathyTpContactList *list); -static void tp_contact_list_request_avatar_cb (DBusGProxy *proxy, - GArray *avatar_data, - gchar *mime_type, - GError *error, - TpContactListAvatarRequestData *data); -static void tp_contact_list_aliases_update_cb (DBusGProxy *proxy, - GPtrArray *handlers, - EmpathyTpContactList *list); -static void tp_contact_list_request_aliases_cb (DBusGProxy *proxy, - gchar **contact_names, - GError *error, - TpContactListAliasesRequestData *data); -static void tp_contact_list_presence_update_cb (DBusGProxy *proxy, - GHashTable *handle_table, - EmpathyTpContactList *list); -static void tp_contact_list_parse_presence_foreach (guint handle, - GValueArray *presence_struct, - EmpathyTpContactList *list); -static void tp_contact_list_presences_table_foreach (const gchar *state_str, - GHashTable *presences_table, - EmpathyPresence **presence); -static void tp_contact_list_status_changed_cb (MissionControl *mc, - TelepathyConnectionStatus status, - McPresence presence, - TelepathyConnectionStatusReason reason, - const gchar *unique_name, - EmpathyTpContactList *list); +static void empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass); +static void empathy_tp_contact_list_init (EmpathyTpContactList *list); +static void tp_contact_list_iface_init (EmpathyContactListIface *iface); enum { DESTROY, LAST_SIGNAL }; -static guint signals[LAST_SIGNAL]; -GList *avatar_requests_queue = NULL; -guint n_avatar_requests = 0; +static guint signals[LAST_SIGNAL]; G_DEFINE_TYPE_WITH_CODE (EmpathyTpContactList, empathy_tp_contact_list, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST, tp_contact_list_iface_init)); static void -empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = tp_contact_list_finalize; - - signals[DESTROY] = - g_signal_new ("destroy", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - - g_type_class_add_private (object_class, sizeof (EmpathyTpContactListPriv)); -} - -static void -tp_contact_list_iface_init (EmpathyContactListIface *iface) -{ - iface->setup = tp_contact_list_setup; - iface->find = tp_contact_list_find; - iface->add = tp_contact_list_add; - iface->remove = tp_contact_list_remove; - iface->get_members = tp_contact_list_get_members; - iface->get_local_pending = tp_contact_list_get_local_pending; - iface->process_pending = tp_contact_list_process_pending; -} - -static void -empathy_tp_contact_list_init (EmpathyTpContactList *list) +tp_contact_list_group_destroy_cb (EmpathyTpGroup *group, + EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; + EmpathyTpContactListPriv *priv = GET_PRIV (list); - priv = GET_PRIV (list); + empathy_debug (DEBUG_DOMAIN, "Group destroyed: %s", + empathy_tp_group_get_name (group)); - priv->groups = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) g_object_unref); - priv->contacts = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) g_object_unref); + priv->groups = g_list_remove (priv->groups, group); + g_object_unref (group); } static void -tp_contact_list_finalize (GObject *object) +tp_contact_list_group_member_added_cb (EmpathyTpGroup *group, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + const gchar *message, + EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - EmpathyTpContactList *list; - - list = EMPATHY_TP_CONTACT_LIST (object); - priv = GET_PRIV (list); - - empathy_debug (DEBUG_DOMAIN, "finalize: %p", object); - - dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), - "AccountStatusChanged", - G_CALLBACK (tp_contact_list_status_changed_cb), - list); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + const gchar *group_name; + GList **groups; - tp_contact_list_finalize_proxies (list); - - if (priv->tp_conn) { - g_object_unref (priv->tp_conn); - } - if (priv->subscribe) { - g_object_unref (priv->subscribe); - } - if (priv->publish) { - g_object_unref (priv->publish); - } - - g_object_unref (priv->account); - g_object_unref (priv->user_contact); - g_object_unref (priv->mc); - g_hash_table_destroy (priv->groups); - g_hash_table_destroy (priv->contacts); - - g_list_foreach (priv->local_pending, (GFunc) empathy_contact_list_info_free, NULL); - g_list_free (priv->local_pending); - - g_list_foreach (priv->members, (GFunc) g_object_unref, NULL); - g_list_free (priv->members); - - G_OBJECT_CLASS (empathy_tp_contact_list_parent_class)->finalize (object); -} - -EmpathyTpContactList * -empathy_tp_contact_list_new (McAccount *account) -{ - EmpathyTpContactListPriv *priv; - EmpathyTpContactList *list; - MissionControl *mc; - McProfile *profile; - const gchar *protocol_name; - guint handle; - GError *error = NULL; - - g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); - - mc = empathy_mission_control_new (); - - if (mission_control_get_connection_status (mc, account, NULL) != 0) { - /* The account is not connected, nothing to do. */ - return NULL; - } - - list = g_object_new (EMPATHY_TYPE_TP_CONTACT_LIST, NULL); - priv = GET_PRIV (list); - - priv->tp_conn = mission_control_get_connection (mc, account, NULL); - priv->account = g_object_ref (account); - priv->mc = mc; - - /* Check for protocols that does not support contact groups. We can - * put all contacts into a special group in that case. - * FIXME: Default group should be an information in the profile */ - profile = mc_account_get_profile (account); - protocol_name = mc_profile_get_protocol_name (profile); - if (strcmp (protocol_name, "local-xmpp") == 0) { - priv->protocol_group = _("People nearby"); - } - g_object_unref (profile); - - g_signal_connect (priv->tp_conn, "destroy", - G_CALLBACK (tp_contact_list_destroy_cb), - list); - dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), - "AccountStatusChanged", - G_CALLBACK (tp_contact_list_status_changed_cb), - list, NULL); - - priv->aliasing_iface = tp_conn_get_interface (priv->tp_conn, - TELEPATHY_CONN_IFACE_ALIASING_QUARK); - priv->avatars_iface = tp_conn_get_interface (priv->tp_conn, - TELEPATHY_CONN_IFACE_AVATARS_QUARK); - priv->presence_iface = tp_conn_get_interface (priv->tp_conn, - TELEPATHY_CONN_IFACE_PRESENCE_QUARK); - - if (priv->aliasing_iface) { - dbus_g_proxy_connect_signal (priv->aliasing_iface, - "AliasesChanged", - G_CALLBACK (tp_contact_list_aliases_update_cb), - list, NULL); - } - - if (priv->avatars_iface) { - dbus_g_proxy_connect_signal (priv->avatars_iface, - "AvatarUpdated", - G_CALLBACK (tp_contact_list_avatar_update_cb), - list, NULL); + if (!g_list_find (priv->members, contact)) { + return; } - if (priv->presence_iface) { - dbus_g_proxy_connect_signal (priv->presence_iface, - "PresenceUpdate", - G_CALLBACK (tp_contact_list_presence_update_cb), - list, NULL); + groups = g_hash_table_lookup (priv->contacts_groups, contact); + if (!groups) { + groups = g_slice_new0 (GList*); + g_hash_table_insert (priv->contacts_groups, + g_object_ref (contact), + groups); } - /* Get our own handle and contact */ - if (!tp_conn_get_self_handle (DBUS_G_PROXY (priv->tp_conn), - &handle, &error)) { - empathy_debug (DEBUG_DOMAIN, "GetSelfHandle Error: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } else { - priv->user_contact = empathy_tp_contact_list_get_from_handle (list, handle); - empathy_contact_set_is_user (priv->user_contact, TRUE); + group_name = empathy_tp_group_get_name (group); + if (!g_list_find_custom (*groups, group_name, (GCompareFunc) strcmp)) { + empathy_debug (DEBUG_DOMAIN, "Contact %s (%d) added to group %s", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact), + group_name); + *groups = g_list_prepend (*groups, g_strdup (group_name)); + g_signal_emit_by_name (list, "groups-changed", contact, + group_name, + TRUE); } - - return list; } static void -tp_contact_list_setup (EmpathyContactList *list) +tp_contact_list_group_member_removed_cb (EmpathyTpGroup *group, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + const gchar *message, + EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - GPtrArray *channels; - GError *error = NULL; - guint i; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - - priv = GET_PRIV (list); - - empathy_debug (DEBUG_DOMAIN, "setup contact list: %p", list); - - priv->setup = TRUE; - dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", - G_CALLBACK (tp_contact_list_newchannel_cb), - list, NULL); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + const gchar *group_name; + GList **groups, *l; - /* Get existing channels */ - if (!tp_conn_list_channels (DBUS_G_PROXY (priv->tp_conn), - &channels, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Failed to get list of open channels: %s", - error ? error->message : "No error given"); - g_clear_error (&error); + if (!g_list_find (priv->members, contact)) { return; } - for (i = 0; channels->len > i; i++) { - GValueArray *chan_struct; - const gchar *object_path; - const gchar *chan_iface; - TelepathyHandleType handle_type; - guint handle; - - chan_struct = g_ptr_array_index (channels, i); - object_path = g_value_get_boxed (g_value_array_get_nth (chan_struct, 0)); - chan_iface = g_value_get_string (g_value_array_get_nth (chan_struct, 1)); - handle_type = g_value_get_uint (g_value_array_get_nth (chan_struct, 2)); - handle = g_value_get_uint (g_value_array_get_nth (chan_struct, 3)); - - tp_contact_list_newchannel_cb (DBUS_G_PROXY (priv->tp_conn), - object_path, chan_iface, - handle_type, handle, - FALSE, - EMPATHY_TP_CONTACT_LIST (list)); - - g_value_array_free (chan_struct); + groups = g_hash_table_lookup (priv->contacts_groups, contact); + if (!groups) { + return; } - g_ptr_array_free (channels, TRUE); -} - -static EmpathyContact * -tp_contact_list_find (EmpathyContactList *list, - const gchar *id) -{ - EmpathyTpContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - return g_hash_table_find (priv->contacts, - (GHRFunc) tp_contact_list_find_foreach, - (gchar*) id); -} - -static void -tp_contact_list_add (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message) -{ - EmpathyTpContactListPriv *priv; - guint handle; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - - priv = GET_PRIV (list); - - handle = empathy_contact_get_handle (contact); - empathy_tp_group_add_member (priv->subscribe, handle, message); -} - -static void -tp_contact_list_remove (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message) -{ - EmpathyTpContactListPriv *priv; - guint handle; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - - priv = GET_PRIV (list); - - handle = empathy_contact_get_handle (contact); - empathy_tp_group_remove_member (priv->subscribe, handle, message); -} - -static GList * -tp_contact_list_get_members (EmpathyContactList *list) -{ - EmpathyTpContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - g_list_foreach (priv->members, (GFunc) g_object_ref, NULL); - return g_list_copy (priv->members); -} - -static GList * -tp_contact_list_get_local_pending (EmpathyContactList *list) -{ - EmpathyTpContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - return g_list_copy (priv->local_pending); -} - -static void -tp_contact_list_process_pending (EmpathyContactList *list, - EmpathyContact *contact, - gboolean accept) -{ - EmpathyTpContactListPriv *priv; - guint handle; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - - priv = GET_PRIV (list); - - handle = empathy_contact_get_handle (contact); - if (accept) { - empathy_tp_group_add_member (priv->publish, handle, NULL); - empathy_tp_group_add_member (priv->subscribe, handle, NULL); - } else { - empathy_tp_group_remove_member (priv->publish, handle, NULL); + group_name = empathy_tp_group_get_name (group); + if ((l = g_list_find_custom (*groups, group_name, (GCompareFunc) strcmp))) { + empathy_debug (DEBUG_DOMAIN, "Contact %s (%d) removed from group %s", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact), + group_name); + *groups = g_list_delete_link (*groups, l); + g_signal_emit_by_name (list, "groups-changed", contact, + group_name, + FALSE); } } -McAccount * -empathy_tp_contact_list_get_account (EmpathyTpContactList *list) -{ - EmpathyTpContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - return priv->account; -} - -EmpathyContact * -empathy_tp_contact_list_get_user (EmpathyTpContactList *list) +static EmpathyTpGroup * +tp_contact_list_find_group (EmpathyTpContactList *list, + const gchar *group) { - EmpathyTpContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GList *l; - priv = GET_PRIV (list); - - return priv->user_contact; + for (l = priv->groups; l; l = l->next) { + if (strcmp (group, empathy_tp_group_get_name (l->data)) == 0) { + return l->data; + } + } + return NULL; } -EmpathyContact * -empathy_tp_contact_list_get_from_id (EmpathyTpContactList *list, - const gchar *id) +static TpContactListType +tp_contact_list_get_type (EmpathyTpContactList *list, + EmpathyTpGroup *group) { EmpathyTpContactListPriv *priv; - EmpathyContact *contact; - const gchar *contact_ids[] = {id, NULL}; - GArray *handles; - guint handle; - GError *error = NULL; + TpContactListType list_type; + const gchar *name; - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - g_return_val_if_fail (id != NULL, NULL); - priv = GET_PRIV (list); - contact = tp_contact_list_find (EMPATHY_CONTACT_LIST (list), id); - if (contact) { - return g_object_ref (contact); - } - - /* The id is unknown, requests a new handle */ - if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_CONTACT, - contact_ids, - &handles, &error)) { - empathy_debug (DEBUG_DOMAIN, - "RequestHandle for %s failed: %s", id, - error ? error->message : "No error given"); - g_clear_error (&error); - return NULL; - } - - handle = g_array_index(handles, guint, 0); - g_array_free (handles, TRUE); - - return empathy_tp_contact_list_get_from_handle (list, handle); -} - -EmpathyContact * -empathy_tp_contact_list_get_from_handle (EmpathyTpContactList *list, - guint handle) -{ - EmpathyContact *contact; - GArray *handles; - GList *contacts; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - handles = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_append_val (handles, handle); - - contacts = empathy_tp_contact_list_get_from_handles (list, handles); - g_array_free (handles, TRUE); - - if (!contacts) { - return NULL; + name = empathy_tp_group_get_name (group); + if (strcmp (name, "subscribe") == 0) { + list_type = TP_CONTACT_LIST_TYPE_SUBSCRIBE; + } else if (strcmp (name, "publish") == 0) { + list_type = TP_CONTACT_LIST_TYPE_PUBLISH; + } else { + list_type = TP_CONTACT_LIST_TYPE_UNKNOWN; } - contact = contacts->data; - g_list_free (contacts); - - return contact; + return list_type; } -GList * -empathy_tp_contact_list_get_from_handles (EmpathyTpContactList *list, - GArray *handles) +static void +tp_contact_list_add_member (EmpathyTpContactList *list, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + const gchar *message) { - EmpathyTpContactListPriv *priv; - gchar **handles_names; - gchar **id; - GArray *new_handles; - GList *contacts = NULL; - guint i; - GError *error = NULL; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - g_return_val_if_fail (handles != NULL, NULL); - - priv = GET_PRIV (list); - - /* Search all handles we already have */ - new_handles = g_array_new (FALSE, FALSE, sizeof (guint)); - for (i = 0; i < handles->len; i++) { - EmpathyContact *contact; - guint handle; - - handle = g_array_index (handles, guint, i); - - if (handle == 0) { - continue; - } - - contact = g_hash_table_lookup (priv->contacts, - GUINT_TO_POINTER (handle)); - - if (contact) { - contacts = g_list_prepend (contacts, - g_object_ref (contact)); - } else { - g_array_append_val (new_handles, handle); - } - } + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GList *l; - if (new_handles->len == 0) { - g_array_free (new_handles, TRUE); - return contacts; - } + /* Add to the list and emit signal */ + priv->members = g_list_prepend (priv->members, g_object_ref (contact)); + g_signal_emit_by_name (list, "members-changed", + contact, actor, reason, message, + TRUE); - /* Holds all handles we don't have yet. - * FIXME: We should release them at some point. */ - if (!tp_conn_hold_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_CONTACT, - new_handles, &error)) { - empathy_debug (DEBUG_DOMAIN, - "HoldHandles Error: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - g_array_free (new_handles, TRUE); - return contacts; + /* This contact is now member, implicitly accept pending. */ + if (g_list_find (priv->pendings, contact)) { + empathy_tp_group_add_member (priv->publish, contact, ""); } - /* Get the IDs of all new handles */ - if (!tp_conn_inspect_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_CONTACT, - new_handles, - &handles_names, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "InspectHandle Error: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - g_array_free (new_handles, TRUE); - return contacts; - } - - /* Create contact objects */ - for (i = 0, id = handles_names; *id && i < new_handles->len; id++, i++) { - EmpathyContact *contact; - guint handle; - - handle = g_array_index (new_handles, guint, i); - contact = g_object_new (EMPATHY_TYPE_CONTACT, - "account", priv->account, - "id", *id, - "handle", handle, - NULL); - - if (priv->protocol_group) { - empathy_contact_add_group (contact, priv->protocol_group); + /* Update groups of the contact */ + for (l = priv->groups; l; l = l->next) { + if (empathy_tp_group_is_member (l->data, contact)) { + tp_contact_list_group_member_added_cb (l->data, contact, + NULL, 0, NULL, + list); } - - if (!priv->presence_iface) { - EmpathyPresence *presence; - - /* We have no presence iface, set default presence - * to available */ - presence = empathy_presence_new_full (MC_PRESENCE_AVAILABLE, - NULL); - - empathy_contact_set_presence (contact, presence); - g_object_unref (presence); - } - - g_signal_connect (contact, "notify::groups", - G_CALLBACK (tp_contact_list_groups_updated_cb), - list); - g_signal_connect (contact, "notify::name", - G_CALLBACK (tp_contact_list_name_updated_cb), - list); - - empathy_debug (DEBUG_DOMAIN, "new contact created: %s (%d)", - *id, handle); - - g_hash_table_insert (priv->contacts, - GUINT_TO_POINTER (handle), - contact); - - contacts = g_list_prepend (contacts, g_object_ref (contact)); } - - tp_contact_list_get_info (list, new_handles); - - g_array_free (new_handles, TRUE); - g_strfreev (handles_names); - - return contacts; } -void -empathy_tp_contact_list_rename_group (EmpathyTpContactList *list, - const gchar *old_group, - const gchar *new_group) +static void +tp_contact_list_added_cb (EmpathyTpGroup *group, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + const gchar *message, + EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - EmpathyTpGroup *group; - GArray *members; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - g_return_if_fail (old_group != NULL); - g_return_if_fail (new_group != NULL); - - priv = GET_PRIV (list); - - group = g_hash_table_find (priv->groups, - (GHRFunc) tp_contact_list_find_group, - (gchar*) old_group); - if (!group) { - /* The group doesn't exists on this account */ - return; - } - - empathy_debug (DEBUG_DOMAIN, "rename group %s to %s", group, new_group); - - /* Remove all members from the old group */ - members = empathy_tp_group_get_members (group); - empathy_tp_group_remove_members (group, members, ""); - tp_contact_list_group_members_removed_cb (group, members, - 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE, - NULL, list); - g_hash_table_remove (priv->groups, - empathy_tp_group_get_object_path (group)); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpContactListType list_type; - /* Add all members to the new group */ - group = tp_contact_list_get_group (list, new_group); - if (group) { - empathy_tp_group_add_members (group, members, ""); + list_type = tp_contact_list_get_type (list, group); + empathy_debug (DEBUG_DOMAIN, "Contact %s (%d) added to list type %d", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact), + list_type); + + /* We now get the presence of that contact, add it to members */ + if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE && + !g_list_find (priv->members, contact)) { + tp_contact_list_add_member (list, contact, actor, reason, message); + } + + /* We now send our presence to that contact, remove it from pendings */ + if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH && + g_list_find (priv->pendings, contact)) { + g_signal_emit_by_name (list, "pendings-changed", + contact, actor, reason, message, + FALSE); + priv->pendings = g_list_remove (priv->pendings, contact); + g_object_unref (contact); } } -GList * -empathy_tp_contact_list_get_groups (EmpathyTpContactList *list) -{ - EmpathyTpContactListPriv *priv; - GList *groups = NULL; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - g_hash_table_foreach (priv->groups, - (GHFunc) tp_contact_list_get_groups_foreach, - &groups); - - groups = g_list_sort (groups, (GCompareFunc) strcmp); - - return groups; -} - static void -tp_contact_list_finalize_proxies (EmpathyTpContactList *list) +tp_contact_list_removed_cb (EmpathyTpGroup *group, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + const gchar *message, + EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - - priv = GET_PRIV (list); - - if (priv->tp_conn) { - g_signal_handlers_disconnect_by_func (priv->tp_conn, - tp_contact_list_destroy_cb, - list); - dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", - G_CALLBACK (tp_contact_list_newchannel_cb), - list); - } - - if (priv->aliasing_iface) { - dbus_g_proxy_disconnect_signal (priv->aliasing_iface, - "AliasesChanged", - G_CALLBACK (tp_contact_list_aliases_update_cb), - list); - } + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpContactListType list_type; - if (priv->avatars_iface) { - dbus_g_proxy_disconnect_signal (priv->avatars_iface, - "AvatarUpdated", - G_CALLBACK (tp_contact_list_avatar_update_cb), - list); + list_type = tp_contact_list_get_type (list, group); + empathy_debug (DEBUG_DOMAIN, "Contact %s (%d) removed from list type %d", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact), + list_type); + + /* This contact refuses to send us his presence, remove from members. */ + if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE && + g_list_find (priv->members, contact)) { + g_signal_emit_by_name (list, "members-changed", + contact, actor, reason, message, + FALSE); + priv->members = g_list_remove (priv->members, contact); + g_object_unref (contact); } - if (priv->presence_iface) { - dbus_g_proxy_disconnect_signal (priv->presence_iface, - "PresenceUpdate", - G_CALLBACK (tp_contact_list_presence_update_cb), - list); + /* We refuse to send our presence to that contact, remove from pendings */ + if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH && + g_list_find (priv->pendings, contact)) { + g_signal_emit_by_name (list, "pendings-changed", + contact, actor, reason, message, + FALSE); + priv->pendings = g_list_remove (priv->pendings, contact); + g_object_unref (contact); } } static void -tp_contact_list_destroy_cb (DBusGProxy *proxy, +tp_contact_list_pending_cb (EmpathyTpGroup *group, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + const gchar *message, EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - - priv = GET_PRIV (list); - - empathy_debug (DEBUG_DOMAIN, "Connection destroyed... " - "Account disconnected or CM crashed"); - - /* DBus proxies should NOT be used anymore */ - g_object_unref (priv->tp_conn); - priv->tp_conn = NULL; - priv->aliasing_iface = NULL; - priv->avatars_iface = NULL; - priv->presence_iface = NULL; - - /* Remove all contacts */ - g_hash_table_foreach (priv->contacts, - (GHFunc) tp_contact_list_contact_removed_foreach, - list); - g_hash_table_remove_all (priv->contacts); - - /* Tell the world to not use us anymore */ - g_signal_emit (list, signals[DESTROY], 0); -} - -static void -tp_contact_list_contact_removed_foreach (guint handle, - EmpathyContact *contact, - EmpathyTpContactList *list) -{ - g_signal_handlers_disconnect_by_func (contact, - tp_contact_list_groups_updated_cb, - list); - g_signal_handlers_disconnect_by_func (contact, - tp_contact_list_name_updated_cb, - list); - - g_signal_emit_by_name (list, "contact-removed", contact); -} - -static void -tp_contact_list_block_contact (EmpathyTpContactList *list, - EmpathyContact *contact) -{ - g_signal_handlers_block_by_func (contact, - tp_contact_list_groups_updated_cb, - list); - g_signal_handlers_block_by_func (contact, - tp_contact_list_name_updated_cb, - list); -} - -static void -tp_contact_list_unblock_contact (EmpathyTpContactList *list, - EmpathyContact *contact) -{ - g_signal_handlers_unblock_by_func (contact, - tp_contact_list_groups_updated_cb, - list); - g_signal_handlers_unblock_by_func (contact, - tp_contact_list_name_updated_cb, - list); -} + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpContactListType list_type; -static gboolean -tp_contact_list_find_foreach (guint handle, - EmpathyContact *contact, - gchar *id) -{ - if (strcmp (empathy_contact_get_id (contact), id) == 0) { - return TRUE; + list_type = tp_contact_list_get_type (list, group); + empathy_debug (DEBUG_DOMAIN, "Contact %s (%d) pending in list type %d", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact), + list_type); + + /* We want this contact in our contact list but we don't get its + * presence yet. Add to members anyway. */ + if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE && + !g_list_find (priv->members, contact)) { + tp_contact_list_add_member (list, contact, actor, reason, message); + } + + /* This contact wants our presence, auto accept if he is member, + * otherwise he is pending. */ + if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH && + !g_list_find (priv->pendings, contact)) { + if (g_list_find (priv->members, contact)) { + empathy_tp_group_add_member (priv->publish, contact, ""); + } else { + priv->pendings = g_list_prepend (priv->pendings, + g_object_ref (contact)); + g_signal_emit_by_name (list, "pendings-changed", + contact, actor, reason, message, + TRUE); + } } - - return FALSE; } static void @@ -955,17 +351,13 @@ tp_contact_list_newchannel_cb (DBusGProxy *proxy, gboolean suppress_handle, EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; + EmpathyTpContactListPriv *priv = GET_PRIV (list); EmpathyTpGroup *group; TpChan *new_chan; const gchar *bus_name; - GArray *members; - - priv = GET_PRIV (list); if (strcmp (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST) != 0 || - suppress_handle || - !priv->setup) { + suppress_handle) { return; } @@ -973,962 +365,629 @@ tp_contact_list_newchannel_cb (DBusGProxy *proxy, new_chan = tp_chan_new (tp_get_bus (), bus_name, object_path, - channel_type, handle_type, channel_handle); + channel_type, + handle_type, + channel_handle); g_return_if_fail (TELEPATHY_IS_CHAN (new_chan)); - if (handle_type == TP_HANDLE_TYPE_LIST) { - TpContactListType list_type; + group = empathy_tp_group_new (priv->account, new_chan); + g_object_unref (new_chan); - group = empathy_tp_group_new (priv->account, new_chan); + if (handle_type == TP_HANDLE_TYPE_LIST) { + TpContactListType list_type; + GList *contacts, *l; list_type = tp_contact_list_get_type (list, group); - if (list_type == TP_CONTACT_LIST_TYPE_UNKNOWN) { - empathy_debug (DEBUG_DOMAIN, - "Type of contact list channel unknown: %s", - empathy_tp_group_get_name (group)); - - g_object_unref (new_chan); - g_object_unref (group); - return; - } else { - empathy_debug (DEBUG_DOMAIN, - "New contact list channel of type: %d", - list_type); - } - - g_signal_connect (group, "members-added", - G_CALLBACK (tp_contact_list_added_cb), - list); - g_signal_connect (group, "members-removed", - G_CALLBACK (tp_contact_list_removed_cb), - list); - - if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) { - GList *pendings, *l; - - if (priv->publish) { - g_object_unref (priv->publish); - } + if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH && !priv->publish) { priv->publish = group; - /* Makes no sense to be in remote-pending */ + /* Publish is the list of contacts to who we send our + * presence. Makes no sense to be in remote-pending */ g_signal_connect (group, "local-pending", G_CALLBACK (tp_contact_list_pending_cb), list); - pendings = empathy_tp_group_get_local_pending_members_with_info (group); - if (pendings) { - GArray *pending; - - pending = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); - for (l = pendings; l; l = l->next) { - EmpathyTpGroupInfo *info; - - info = l->data; - - g_array_insert_val (pending, 0, info->member); - tp_contact_list_pending_cb (group, pending, - info->actor, - info->reason, - info->message, - list); - } - g_array_free (pending, TRUE); - empathy_tp_group_info_list_free (pendings); + contacts = empathy_tp_group_get_local_pendings (group); + for (l = contacts; l; l = l->next) { + EmpathyPendingInfo *info = l->data; + + tp_contact_list_pending_cb (group, + info->member, + info->actor, + 0, + info->message, + list); + empathy_pending_info_free (info); } + g_list_free (contacts); } - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - GArray *remote_pendings = NULL; - - if (priv->subscribe) { - g_object_unref (priv->subscribe); - } + else if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE && !priv->subscribe) { priv->subscribe = group; - /* Makes no sense to be in local-pending */ + /* Subscribe is the list of contacts from who we + * receive presence. Makes no sense to be in + * local-pending */ g_signal_connect (group, "remote-pending", G_CALLBACK (tp_contact_list_pending_cb), list); - empathy_tp_group_get_all_members (group, - &members, - NULL, - &remote_pendings); - - tp_contact_list_pending_cb (group, remote_pendings, 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE, - NULL, - list); - g_array_free (remote_pendings, TRUE); + + contacts = empathy_tp_group_get_remote_pendings (group); + for (l = contacts; l; l = l->next) { + tp_contact_list_pending_cb (group, + l->data, + NULL, 0, + NULL, list); + g_object_unref (l->data); + } + g_list_free (contacts); } else { - members = empathy_tp_group_get_members (group); + empathy_debug (DEBUG_DOMAIN, + "Type of contact list channel unknown " + "or aleady have that list: %s", + empathy_tp_group_get_name (group)); + g_object_unref (group); + return; } + empathy_debug (DEBUG_DOMAIN, + "New contact list channel of type: %d", + list_type); + + g_signal_connect (group, "member-added", + G_CALLBACK (tp_contact_list_added_cb), + list); + g_signal_connect (group, "member-removed", + G_CALLBACK (tp_contact_list_removed_cb), + list); - tp_contact_list_added_cb (group, members, 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE, - NULL, list); - g_array_free (members, TRUE); + contacts = empathy_tp_group_get_members (group); + for (l = contacts; l; l = l->next) { + tp_contact_list_added_cb (group, + l->data, + NULL, 0, NULL, + list); + g_object_unref (l->data); + } + g_list_free (contacts); } else if (handle_type == TP_HANDLE_TYPE_GROUP) { - const gchar *object_path; + const gchar *group_name; + GList *contacts, *l; - object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (new_chan)); - if (g_hash_table_lookup (priv->groups, object_path)) { - g_object_unref (new_chan); + /* Check if already exists */ + group_name = empathy_tp_group_get_name (group); + if (tp_contact_list_find_group (list, group_name)) { + g_object_unref (group); return; } - group = empathy_tp_group_new (priv->account, new_chan); - empathy_debug (DEBUG_DOMAIN, "New server-side group channel: %s", - empathy_tp_group_get_name (group)); + group_name); - dbus_g_proxy_connect_signal (DBUS_G_PROXY (new_chan), "Closed", - G_CALLBACK - (tp_contact_list_group_channel_closed_cb), - list, NULL); + priv->groups = g_list_prepend (priv->groups, group); - g_hash_table_insert (priv->groups, g_strdup (object_path), group); - g_signal_connect (group, "members-added", - G_CALLBACK (tp_contact_list_group_members_added_cb), + g_signal_connect (group, "member-added", + G_CALLBACK (tp_contact_list_group_member_added_cb), list); - g_signal_connect (group, "members-removed", - G_CALLBACK (tp_contact_list_group_members_removed_cb), + g_signal_connect (group, "member-removed", + G_CALLBACK (tp_contact_list_group_member_removed_cb), + list); + g_signal_connect (group, "destroy", + G_CALLBACK (tp_contact_list_group_destroy_cb), list); - members = empathy_tp_group_get_members (group); - tp_contact_list_group_members_added_cb (group, members, 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE, - NULL, list); - g_array_free (members, TRUE); + contacts = empathy_tp_group_get_members (group); + for (l = contacts; l; l = l->next) { + tp_contact_list_group_member_added_cb (group, l->data, + NULL, 0, NULL, + list); + g_object_unref (l->data); + } + g_list_free (contacts); + } else { + empathy_debug (DEBUG_DOMAIN, + "Unknown handle type (%d) for contact list channel", + handle_type); + g_object_unref (group); } - - g_object_unref (new_chan); } -static TpContactListType -tp_contact_list_get_type (EmpathyTpContactList *list, - EmpathyTpGroup *group) +static void +tp_contact_list_remove_all (EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - TpContactListType list_type; - const gchar *name; - - priv = GET_PRIV (list); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GList *l; - name = empathy_tp_group_get_name (group); - if (strcmp (name, "subscribe") == 0) { - list_type = TP_CONTACT_LIST_TYPE_SUBSCRIBE; - } else if (strcmp (name, "publish") == 0) { - list_type = TP_CONTACT_LIST_TYPE_PUBLISH; - } else { - list_type = TP_CONTACT_LIST_TYPE_UNKNOWN; + for (l = priv->members; l; l = l->next) { + g_signal_emit_by_name (list, "members-changed", l->data, + NULL, 0, NULL, + FALSE); + g_object_unref (l->data); + } + for (l = priv->pendings; l; l = l->next) { + g_signal_emit_by_name (list, "pendings-changed", l->data, + NULL, 0, NULL, + FALSE); + g_object_unref (l->data); } - return list_type; + g_list_free (priv->members); + g_list_free (priv->pendings); + priv->members = NULL; + priv->pendings = NULL; } static void -tp_contact_list_added_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_destroy_cb (TpConn *tp_conn, + EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - GList *added_list, *l; - TpContactListType list_type; + EmpathyTpContactListPriv *priv = GET_PRIV (list); - priv = GET_PRIV (list); + empathy_debug (DEBUG_DOMAIN, "Account disconnected or CM crashed"); - list_type = tp_contact_list_get_type (list, group); - - added_list = empathy_tp_contact_list_get_from_handles (list, handles); - for (l = added_list; l; l = l->next) { - EmpathyContact *contact; - EmpathySubscription subscription; - - contact = EMPATHY_CONTACT (l->data); - - empathy_debug (DEBUG_DOMAIN, "Contact '%s' added to list type %d", - empathy_contact_get_name (contact), - list_type); + /* DBus proxie should NOT be used anymore */ + g_object_unref (priv->tp_conn); + priv->tp_conn = NULL; - subscription = empathy_contact_get_subscription (contact); - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - subscription |= EMPATHY_SUBSCRIPTION_FROM; - } - else if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) { - subscription |= EMPATHY_SUBSCRIPTION_TO; - tp_contact_list_remove_local_pending (list, contact); - } + tp_contact_list_remove_all (list); - tp_contact_list_block_contact (list, contact); - empathy_contact_set_subscription (contact, subscription); - tp_contact_list_unblock_contact (list, contact); + /* Tell the world to not use us anymore */ + g_signal_emit (list, signals[DESTROY], 0); +} - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - if (!g_list_find (priv->members, contact)) { - priv->members = g_list_prepend (priv->members, - g_object_ref (contact)); - g_signal_emit_by_name (list, "contact-added", contact); - } - } +static void +tp_contact_list_disconnect (EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv = GET_PRIV (list); - g_object_unref (contact); + if (priv->tp_conn) { + g_signal_handlers_disconnect_by_func (priv->tp_conn, + tp_contact_list_destroy_cb, + list); + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", + G_CALLBACK (tp_contact_list_newchannel_cb), + list); } - - g_list_free (added_list); } static void -tp_contact_list_removed_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_status_changed_cb (MissionControl *mc, + TelepathyConnectionStatus status, + McPresence presence, + TelepathyConnectionStatusReason reason, + const gchar *unique_name, + EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - GList *removed_list, *l; - TpContactListType list_type; - - priv = GET_PRIV (list); - - list_type = tp_contact_list_get_type (list, group); - - removed_list = empathy_tp_contact_list_get_from_handles (list, handles); - for (l = removed_list; l; l = l->next) { - EmpathyContact *contact; - EmpathySubscription subscription; - - contact = EMPATHY_CONTACT (l->data); - - empathy_debug (DEBUG_DOMAIN, "Contact '%s' removed from list type %d", - empathy_contact_get_name (contact), - list_type); - - subscription = empathy_contact_get_subscription (contact); - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - subscription &= !EMPATHY_SUBSCRIPTION_FROM; - } - else if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) { - subscription &= !EMPATHY_SUBSCRIPTION_TO; - tp_contact_list_remove_local_pending (list, contact); - } - - tp_contact_list_block_contact (list, contact); - empathy_contact_set_subscription (contact, subscription); - tp_contact_list_unblock_contact (list, contact); - - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - GList *l; + EmpathyTpContactListPriv *priv = GET_PRIV (list); + McAccount *account; - if ((l = g_list_find (priv->members, contact))) { - g_signal_emit_by_name (list, "contact-removed", contact); - priv->members = g_list_delete_link (priv->members, l); - g_object_unref (contact); - } - } - g_object_unref (contact); + account = mc_account_lookup (unique_name); + if (status != TP_CONN_STATUS_CONNECTED && + empathy_account_equal (account, priv->account)) { + /* We are disconnected */ + tp_contact_list_disconnect (list); + tp_contact_list_destroy_cb (priv->tp_conn, list); } - g_list_free (removed_list); + g_object_unref (account); } static void -tp_contact_list_pending_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_group_list_free (GList **groups) +{ + g_list_foreach (*groups, (GFunc) g_free, NULL); + g_list_free (*groups); + g_slice_free (GList*, groups); +} + +static void +tp_contact_list_finalize (GObject *object) { EmpathyTpContactListPriv *priv; - GList *pending_list, *l; - TpContactListType list_type; + EmpathyTpContactList *list; + list = EMPATHY_TP_CONTACT_LIST (object); priv = GET_PRIV (list); - list_type = tp_contact_list_get_type (list, group); - - pending_list = empathy_tp_contact_list_get_from_handles (list, handles); - for (l = pending_list; l; l = l->next) { - EmpathyContact *contact; - - contact = EMPATHY_CONTACT (l->data); - - empathy_debug (DEBUG_DOMAIN, "Contact '%s' pending in list type %d", - empathy_contact_get_name (contact), - list_type); - - if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) { - if (!g_list_find (priv->members, contact)) { - EmpathyContactListInfo *info; - - info = empathy_contact_list_info_new (contact, message); - priv->local_pending = g_list_prepend (priv->local_pending, - info); + empathy_debug (DEBUG_DOMAIN, "finalize: %p", object); - g_signal_emit_by_name (list, "local-pending", - contact, message); - } else { - guint handle; + tp_contact_list_disconnect (list); + tp_contact_list_remove_all (list); - /* That contact wants our presence and he is - * in our roster. Accept to publish our presence - * without asking the user. */ - handle = empathy_contact_get_handle (contact); - empathy_tp_group_add_member (priv->publish, - handle, ""); - } - } - else if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - if (!g_list_find (priv->members, contact)) { - priv->members = g_list_prepend (priv->members, - g_object_ref (contact)); - g_signal_emit_by_name (list, "contact-added", contact); - } - } + if (priv->mc) { + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), + "AccountStatusChanged", + G_CALLBACK (tp_contact_list_status_changed_cb), + list); + g_object_unref (priv->mc); + } - g_object_unref (contact); + if (priv->subscribe) { + g_object_unref (priv->subscribe); + } + if (priv->publish) { + g_object_unref (priv->publish); } + if (priv->account) { + g_object_unref (priv->account); + } + if (priv->tp_conn) { + g_object_unref (priv->tp_conn); + } + + g_hash_table_destroy (priv->contacts_groups); + g_list_foreach (priv->groups, (GFunc) g_object_unref, NULL); + g_list_free (priv->groups); - g_list_free (pending_list); + G_OBJECT_CLASS (empathy_tp_contact_list_parent_class)->finalize (object); } static void -tp_contact_list_remove_local_pending (EmpathyTpContactList *list, - EmpathyContact *contact) +empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass) { - EmpathyTpContactListPriv *priv; - GList *l; - - priv = GET_PRIV (list); + GObjectClass *object_class = G_OBJECT_CLASS (klass); - for (l = priv->local_pending; l; l = l->next) { - EmpathyContactListInfo *info; + object_class->finalize = tp_contact_list_finalize; - info = l->data; - if (empathy_contact_equal (contact, info->contact)) { - empathy_debug (DEBUG_DOMAIN, "Contact no more local-pending: %s", - empathy_contact_get_name (contact)); + signals[DESTROY] = + g_signal_new ("destroy", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); - priv->local_pending = g_list_delete_link (priv->local_pending, l); - empathy_contact_list_info_free (info); - break; - } - } + g_type_class_add_private (object_class, sizeof (EmpathyTpContactListPriv)); } static void -tp_contact_list_groups_updated_cb (EmpathyContact *contact, - GParamSpec *param, - EmpathyTpContactList *list) +empathy_tp_contact_list_init (EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - TpContactListData data; - GList *groups, *l; - - priv = GET_PRIV (list); - - /* Make sure all groups are created */ - groups = empathy_contact_get_groups (contact); - for (l = groups; l; l = l->next) { - tp_contact_list_get_group (list, l->data); - } - - data.handle = empathy_contact_get_handle (contact); - data.new_groups = groups; - - g_hash_table_foreach (priv->groups, - (GHFunc) tp_contact_list_update_groups_foreach, - &data); } static void -tp_contact_list_name_updated_cb (EmpathyContact *contact, - GParamSpec *param, - EmpathyTpContactList *list) +tp_contact_list_setup (EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - GHashTable *new_alias; - const gchar *new_name; - guint handle; + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GPtrArray *channels; + guint i; GError *error = NULL; - priv = GET_PRIV (list); - - if (!priv->aliasing_iface) { - return; - } - - handle = empathy_contact_get_handle (contact); - new_name = empathy_contact_get_name (contact); - - empathy_debug (DEBUG_DOMAIN, "renaming handle %d to %s", - handle, new_name); - - new_alias = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - g_free); - - g_hash_table_insert (new_alias, - GUINT_TO_POINTER (handle), - g_strdup (new_name)); + g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - if (!tp_conn_iface_aliasing_set_aliases (priv->aliasing_iface, - new_alias, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Couldn't rename contact: %s", + /* Get existing channels */ + if (!tp_conn_list_channels (DBUS_G_PROXY (priv->tp_conn), + &channels, + &error)) { + empathy_debug (DEBUG_DOMAIN, + "Failed to get list of open channels: %s", error ? error->message : "No error given"); g_clear_error (&error); + return; } - g_hash_table_destroy (new_alias); -} - -static void -tp_contact_list_update_groups_foreach (gchar *object_path, - EmpathyTpGroup *group, - TpContactListData *data) -{ - gboolean is_member; - gboolean found = FALSE; - const gchar *group_name; - GList *l; - - is_member = empathy_tp_group_is_member (group, data->handle); - group_name = empathy_tp_group_get_name (group); + for (i = 0; i < channels->len; i++) { + GValueArray *chan_struct; + const gchar *object_path; + const gchar *chan_iface; + TelepathyHandleType handle_type; + guint handle; - for (l = data->new_groups; l; l = l->next) { - if (strcmp (group_name, l->data) == 0) { - found = TRUE; - break; - } - } + chan_struct = g_ptr_array_index (channels, i); + object_path = g_value_get_boxed (g_value_array_get_nth (chan_struct, 0)); + chan_iface = g_value_get_string (g_value_array_get_nth (chan_struct, 1)); + handle_type = g_value_get_uint (g_value_array_get_nth (chan_struct, 2)); + handle = g_value_get_uint (g_value_array_get_nth (chan_struct, 3)); - if (is_member && !found) { - /* We are no longer member of this group */ - empathy_debug (DEBUG_DOMAIN, "Contact %d removed from group '%s'", - data->handle, group_name); - empathy_tp_group_remove_member (group, data->handle, ""); - } + tp_contact_list_newchannel_cb (DBUS_G_PROXY (priv->tp_conn), + object_path, chan_iface, + handle_type, handle, + FALSE, + list); - if (!is_member && found) { - /* We are now member of this group */ - empathy_debug (DEBUG_DOMAIN, "Contact %d added to group '%s'", - data->handle, group_name); - empathy_tp_group_add_member (group, data->handle, ""); + g_value_array_free (chan_struct); } + g_ptr_array_free (channels, TRUE); } -static EmpathyTpGroup * -tp_contact_list_get_group (EmpathyTpContactList *list, - const gchar *name) +EmpathyTpContactList * +empathy_tp_contact_list_new (McAccount *account) { EmpathyTpContactListPriv *priv; - EmpathyTpGroup *group; - TpChan *group_channel; - GArray *handles; - guint group_handle; - char *group_object_path; - const char *names[2] = {name, NULL}; - GError *error = NULL; - - priv = GET_PRIV (list); + EmpathyTpContactList *list; + MissionControl *mc; + TpConn *tp_conn = NULL; + McProfile *profile; + const gchar *protocol_name; - group = g_hash_table_find (priv->groups, - (GHRFunc) tp_contact_list_find_group, - (gchar*) name); - if (group) { - return group; - } + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); - empathy_debug (DEBUG_DOMAIN, "creating new group: %s", name); + mc = empathy_mission_control_new (); - if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_GROUP, - names, - &handles, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Couldn't request the creation of a new handle for group: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - return NULL; + /* status==0 means CONNECTED */ + if (mission_control_get_connection_status (mc, account, NULL) == 0) { + tp_conn = mission_control_get_connection (mc, account, NULL); } - group_handle = g_array_index (handles, guint, 0); - g_array_free (handles, TRUE); - - if (!tp_conn_request_channel (DBUS_G_PROXY (priv->tp_conn), - TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, - TP_HANDLE_TYPE_GROUP, - group_handle, - FALSE, - &group_object_path, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Couldn't request the creation of a new group channel: %s", - error ? error->message : "No error given"); - g_clear_error (&error); + if (!tp_conn) { + /* The account is not connected, nothing to do. */ + g_object_unref (mc); return NULL; } - group_channel = tp_chan_new (tp_get_bus (), - dbus_g_proxy_get_bus_name (DBUS_G_PROXY (priv->tp_conn)), - group_object_path, - TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, - TP_HANDLE_TYPE_GROUP, - group_handle); - - dbus_g_proxy_connect_signal (DBUS_G_PROXY (group_channel), - "Closed", - G_CALLBACK - (tp_contact_list_group_channel_closed_cb), - list, - NULL); - - group = empathy_tp_group_new (priv->account, group_channel); - g_hash_table_insert (priv->groups, group_object_path, group); - g_signal_connect (group, "members-added", - G_CALLBACK (tp_contact_list_group_members_added_cb), - list); - g_signal_connect (group, "members-removed", - G_CALLBACK (tp_contact_list_group_members_removed_cb), - list); + list = g_object_new (EMPATHY_TYPE_TP_CONTACT_LIST, NULL); + priv = GET_PRIV (list); - return group; -} + priv->tp_conn = tp_conn; + priv->account = g_object_ref (account); + priv->mc = mc; + priv->contacts_groups = g_hash_table_new_full (empathy_contact_hash, + empathy_contact_equal, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) tp_contact_list_group_list_free); -static gboolean -tp_contact_list_find_group (gchar *key, - EmpathyTpGroup *group, - gchar *group_name) -{ - if (strcmp (group_name, empathy_tp_group_get_name (group)) == 0) { - return TRUE; + /* Check for protocols that does not support contact groups. We can + * put all contacts into a special group in that case. + * FIXME: Default group should be an information in the profile */ + profile = mc_account_get_profile (account); + protocol_name = mc_profile_get_protocol_name (profile); + if (strcmp (protocol_name, "local-xmpp") == 0) { + priv->protocol_group = _("People nearby"); } + g_object_unref (profile); - return FALSE; -} + /* Connect signals */ + dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), + "AccountStatusChanged", + G_CALLBACK (tp_contact_list_status_changed_cb), + list, NULL); + dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", + G_CALLBACK (tp_contact_list_newchannel_cb), + list, NULL); + g_signal_connect (priv->tp_conn, "destroy", + G_CALLBACK (tp_contact_list_destroy_cb), + list); -static void -tp_contact_list_get_groups_foreach (gchar *key, - EmpathyTpGroup *group, - GList **groups) -{ - const gchar *name; + tp_contact_list_setup (list); - name = empathy_tp_group_get_name (group); - *groups = g_list_append (*groups, g_strdup (name)); + return list; } -static void -tp_contact_list_group_channel_closed_cb (TpChan *channel, - EmpathyTpContactList *list) +McAccount * +empathy_tp_contact_list_get_account (EmpathyTpContactList *list) { EmpathyTpContactListPriv *priv; + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + priv = GET_PRIV (list); - g_hash_table_remove (priv->groups, - dbus_g_proxy_get_path (DBUS_G_PROXY (channel))); + return priv->account; } static void -tp_contact_list_group_members_added_cb (EmpathyTpGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_add (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *message) { - EmpathyTpContactListPriv *priv; - GList *added_list, *l; - const gchar *group_name; - - priv = GET_PRIV (list); + EmpathyTpContactListPriv *priv = GET_PRIV (list); - group_name = empathy_tp_group_get_name (group); - added_list = empathy_tp_contact_list_get_from_handles (list, members); - - for (l = added_list; l; l = l->next) { - EmpathyContact *contact; - - contact = EMPATHY_CONTACT (l->data); - - tp_contact_list_block_contact (list, contact); - empathy_contact_add_group (contact, group_name); - tp_contact_list_unblock_contact (list, contact); + g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - g_object_unref (contact); + empathy_tp_group_add_member (priv->subscribe, contact, message); + if (g_list_find (priv->pendings, contact)) { + empathy_tp_group_add_member (priv->publish, contact, message); } - - g_list_free (added_list); } static void -tp_contact_list_group_members_removed_cb (EmpathyTpGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_remove (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *message) { - EmpathyTpContactListPriv *priv; - GList *removed_list, *l; - const gchar *group_name; - - priv = GET_PRIV (list); - - group_name = empathy_tp_group_get_name (group); - removed_list = empathy_tp_contact_list_get_from_handles (list, members); + EmpathyTpContactListPriv *priv = GET_PRIV (list); - for (l = removed_list; l; l = l->next) { - EmpathyContact *contact; - - contact = l->data; - - tp_contact_list_block_contact (list, contact); - empathy_contact_remove_group (contact, group_name); - tp_contact_list_unblock_contact (list, contact); - - g_object_unref (contact); - } + g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - g_list_free (removed_list); + empathy_tp_group_remove_member (priv->subscribe, contact, message); + empathy_tp_group_remove_member (priv->publish, contact, message); } -static void -tp_contact_list_get_info (EmpathyTpContactList *list, - GArray *handles) +static GList * +tp_contact_list_get_members (EmpathyContactList *list) { - EmpathyTpContactListPriv *priv; - GError *error = NULL; + EmpathyTpContactListPriv *priv = GET_PRIV (list); - priv = GET_PRIV (list); - - if (priv->presence_iface) { - /* FIXME: We should use GetPresence instead */ - if (!tp_conn_iface_presence_request_presence (priv->presence_iface, - handles, &error)) { - empathy_debug (DEBUG_DOMAIN, - "Could not request presences: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } - } - - if (priv->aliasing_iface) { - TpContactListAliasesRequestData *data; - - data = g_slice_new (TpContactListAliasesRequestData); - data->list = list; - data->handles = g_memdup (handles->data, handles->len * sizeof (guint)); - - tp_conn_iface_aliasing_request_aliases_async (priv->aliasing_iface, - handles, - (tp_conn_iface_aliasing_request_aliases_reply) - tp_contact_list_request_aliases_cb, - data); - } - - if (priv->avatars_iface) { - guint i; - - for (i = 0; i < handles->len; i++) { - guint handle; + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - handle = g_array_index (handles, gint, i); - tp_contact_list_request_avatar (list, handle); - } - } + g_list_foreach (priv->members, (GFunc) g_object_ref, NULL); + return g_list_copy (priv->members); } -static void -tp_contact_list_request_avatar (EmpathyTpContactList *list, - guint handle) +static GList * +tp_contact_list_get_pendings (EmpathyContactList *list) { - EmpathyTpContactListPriv *priv; - TpContactListAvatarRequestData *data; + EmpathyTpContactListPriv *priv = GET_PRIV (list); - priv = GET_PRIV (list); - - /* We queue avatar requests to not send too many dbus async - * calls at once. If we don't we reach the dbus's limit of - * pending calls */ - data = g_slice_new (TpContactListAvatarRequestData); - data->list = g_object_ref (list); - data->handle = handle; - avatar_requests_queue = g_list_append (avatar_requests_queue, data); - tp_contact_list_start_avatar_requests (); -} + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); -static void -tp_contact_list_start_avatar_requests (void) -{ - empathy_debug (DEBUG_DOMAIN, "Start avatar requests, pending calls: %d", - n_avatar_requests); - - while (n_avatar_requests < MAX_AVATAR_REQUESTS && avatar_requests_queue) { - EmpathyTpContactListPriv *priv; - TpContactListAvatarRequestData *data; - - data = avatar_requests_queue->data; - priv = GET_PRIV (data->list); - - n_avatar_requests++; - avatar_requests_queue = g_list_delete_link (avatar_requests_queue, - avatar_requests_queue); - - empathy_debug (DEBUG_DOMAIN, "Calling RequestAvatar async"); - tp_conn_iface_avatars_request_avatar_async (priv->avatars_iface, - data->handle, - (tp_conn_iface_avatars_request_avatar_reply) - tp_contact_list_request_avatar_cb, - data); - } + g_list_foreach (priv->pendings, (GFunc) g_object_ref, NULL); + return g_list_copy (priv->pendings); } -static void -tp_contact_list_avatar_update_cb (DBusGProxy *proxy, - guint handle, - gchar *new_token, - EmpathyTpContactList *list) +static GList * +tp_contact_list_get_all_groups (EmpathyContactList *list) { - EmpathyTpContactListPriv *priv; + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GList *groups = NULL, *l; - priv = GET_PRIV (list); + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - if (!g_hash_table_lookup (priv->contacts, GUINT_TO_POINTER (handle))) { - /* We don't know this contact, skip */ - return; - } + for (l = priv->groups; l; l = l->next) { + const gchar *name; - empathy_debug (DEBUG_DOMAIN, "Changing avatar for %d to %s", - handle, new_token); + name = empathy_tp_group_get_name (l->data); + groups = g_list_prepend (groups, g_strdup (name)); + } - tp_contact_list_request_avatar (list, handle); + return groups; } -static void -tp_contact_list_request_avatar_cb (DBusGProxy *proxy, - GArray *avatar_data, - gchar *mime_type, - GError *error, - TpContactListAvatarRequestData *data) +static GList * +tp_contact_list_get_groups (EmpathyContactList *list, + EmpathyContact *contact) { - EmpathyContact *contact; - - contact = empathy_tp_contact_list_get_from_handle (data->list, data->handle); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GList **groups; + GList *ret = NULL, *l; - if (error) { - empathy_debug (DEBUG_DOMAIN, "Error requesting avatar for %s: %s", - empathy_contact_get_name (contact), - error ? error->message : "No error given"); - } else { - EmpathyAvatar *avatar; + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - empathy_debug (DEBUG_DOMAIN, "Avatar received for %s (%d)", - empathy_contact_get_id (contact), - data->handle); - - avatar = empathy_avatar_new (avatar_data->data, - avatar_data->len, - mime_type); - tp_contact_list_block_contact (data->list, contact); - empathy_contact_set_avatar (contact, avatar); - tp_contact_list_unblock_contact (data->list, contact); - empathy_avatar_unref (avatar); + groups = g_hash_table_lookup (priv->contacts_groups, contact); + if (!groups) { + return NULL; } - n_avatar_requests--; - tp_contact_list_start_avatar_requests (); + for (l = *groups; l; l = l->next) { + ret = g_list_prepend (ret, g_strdup (l->data)); + } - g_object_unref (contact); - g_object_unref (data->list); - g_slice_free (TpContactListAvatarRequestData, data); + return ret; } -static void -tp_contact_list_aliases_update_cb (DBusGProxy *proxy, - GPtrArray *renamed_handlers, - EmpathyTpContactList *list) +static EmpathyTpGroup * +tp_contact_list_get_group (EmpathyTpContactList *list, + const gchar *group) { - EmpathyTpContactListPriv *priv; - guint i; - - priv = GET_PRIV (list); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + EmpathyTpGroup *tp_group; + gchar *object_path; + guint handle; + GArray *handles; + const char *names[2] = {group, NULL}; + GError *error = NULL; - for (i = 0; renamed_handlers->len > i; i++) { - guint handle; - const gchar *alias; - GValueArray *renamed_struct; - EmpathyContact *contact; + tp_group = tp_contact_list_find_group (list, group); + if (tp_group) { + return tp_group; + } - renamed_struct = g_ptr_array_index (renamed_handlers, i); - handle = g_value_get_uint(g_value_array_get_nth (renamed_struct, 0)); - alias = g_value_get_string(g_value_array_get_nth (renamed_struct, 1)); + empathy_debug (DEBUG_DOMAIN, "creating new group: %s", group); - if (!g_hash_table_lookup (priv->contacts, GUINT_TO_POINTER (handle))) { - /* We don't know this contact, skip */ - continue; - } + if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn), + TP_HANDLE_TYPE_GROUP, + names, + &handles, + &error)) { + empathy_debug (DEBUG_DOMAIN, + "Failed to RequestHandles: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + return NULL; + } + handle = g_array_index (handles, guint, 0); + g_array_free (handles, TRUE); - if (G_STR_EMPTY (alias)) { - alias = NULL; - } + if (!tp_conn_request_channel (DBUS_G_PROXY (priv->tp_conn), + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_GROUP, + handle, + FALSE, + &object_path, + &error)) { + empathy_debug (DEBUG_DOMAIN, + "Failed to RequestChannel: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + return NULL; + } - contact = empathy_tp_contact_list_get_from_handle (list, handle); - tp_contact_list_block_contact (list, contact); - empathy_contact_set_name (contact, alias); - tp_contact_list_unblock_contact (list, contact); - g_object_unref (contact); + tp_contact_list_newchannel_cb (DBUS_G_PROXY (priv->tp_conn), + object_path, + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_GROUP, + handle, FALSE, + list); + g_free (object_path); - empathy_debug (DEBUG_DOMAIN, "contact %d renamed to %s (update cb)", - handle, alias); - } + return tp_contact_list_find_group (list, group); } static void -tp_contact_list_request_aliases_cb (DBusGProxy *proxy, - gchar **contact_names, - GError *error, - TpContactListAliasesRequestData *data) +tp_contact_list_add_to_group (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *group) { - guint i = 0; - gchar **name; + EmpathyTpGroup *tp_group; - if (error) { - empathy_debug (DEBUG_DOMAIN, "Error requesting aliases: %s", - error->message); - } - - for (name = contact_names; *name && !error; name++) { - EmpathyContact *contact; - - contact = empathy_tp_contact_list_get_from_handle (data->list, - data->handles[i]); - tp_contact_list_block_contact (data->list, contact); - empathy_contact_set_name (contact, *name); - tp_contact_list_unblock_contact (data->list, contact); - g_object_unref (contact); - - empathy_debug (DEBUG_DOMAIN, "contact %d renamed to %s (request cb)", - data->handles[i], *name); + g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - i++; - } + tp_group = tp_contact_list_get_group (EMPATHY_TP_CONTACT_LIST (list), + group); - g_free (data->handles); - g_slice_free (TpContactListAliasesRequestData, data); + empathy_tp_group_add_member (tp_group, contact, ""); } static void -tp_contact_list_presence_update_cb (DBusGProxy *proxy, - GHashTable *handle_table, - EmpathyTpContactList *list) +tp_contact_list_remove_from_group (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *group) { - g_hash_table_foreach (handle_table, - (GHFunc) tp_contact_list_parse_presence_foreach, - list); -} + EmpathyTpGroup *tp_group; -static void -tp_contact_list_parse_presence_foreach (guint handle, - GValueArray *presence_struct, - EmpathyTpContactList *list) -{ - EmpathyTpContactListPriv *priv; - GHashTable *presences_table; - EmpathyContact *contact; - EmpathyPresence *presence = NULL; + g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - priv = GET_PRIV (list); + tp_group = tp_contact_list_find_group (EMPATHY_TP_CONTACT_LIST (list), + group); - if (!g_hash_table_lookup (priv->contacts, GUINT_TO_POINTER (handle))) { - /* We don't know this contact, skip */ - return; + if (tp_group) { + empathy_tp_group_remove_member (tp_group, contact, ""); } - - contact = empathy_tp_contact_list_get_from_handle (list, handle); - presences_table = g_value_get_boxed (g_value_array_get_nth (presence_struct, 1)); - - g_hash_table_foreach (presences_table, - (GHFunc) tp_contact_list_presences_table_foreach, - &presence); - - empathy_debug (DEBUG_DOMAIN, "Presence changed for %s (%d) to %s (%d)", - empathy_contact_get_name (contact), - handle, - presence ? empathy_presence_get_status (presence) : "unset", - presence ? empathy_presence_get_state (presence) : MC_PRESENCE_UNSET); - - tp_contact_list_block_contact (list, contact); - empathy_contact_set_presence (contact, presence); - tp_contact_list_unblock_contact (list, contact); - - g_object_unref (contact); } static void -tp_contact_list_presences_table_foreach (const gchar *state_str, - GHashTable *presences_table, - EmpathyPresence **presence) +tp_contact_list_rename_group (EmpathyContactList *list, + const gchar *old_group, + const gchar *new_group) { - McPresence state; - const GValue *message; + EmpathyTpGroup *tp_group; + GList *members; - state = empathy_presence_state_from_str (state_str); - if ((state == MC_PRESENCE_UNSET) || (state == MC_PRESENCE_OFFLINE)) { + g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); + + tp_group = tp_contact_list_find_group (EMPATHY_TP_CONTACT_LIST (list), + old_group); + if (!tp_group) { return; } - if (*presence) { - g_object_unref (*presence); - *presence = NULL; - } + empathy_debug (DEBUG_DOMAIN, "rename group %s to %s", old_group, new_group); - *presence = empathy_presence_new (); - empathy_presence_set_state (*presence, state); + /* Remove all members from the old group */ + members = empathy_tp_group_get_members (tp_group); + empathy_tp_group_remove_members (tp_group, members, ""); + empathy_tp_group_close (tp_group); - message = g_hash_table_lookup (presences_table, "message"); - if (message != NULL) { - empathy_presence_set_status (*presence, - g_value_get_string (message)); - } + /* Add all members to the new group */ + tp_group = tp_contact_list_get_group (EMPATHY_TP_CONTACT_LIST (list), + new_group); + empathy_tp_group_add_members (tp_group, members, ""); + + g_list_foreach (members, (GFunc) g_object_unref, NULL); + g_list_free (members); } static void -tp_contact_list_status_changed_cb (MissionControl *mc, - TelepathyConnectionStatus status, - McPresence presence, - TelepathyConnectionStatusReason reason, - const gchar *unique_name, - EmpathyTpContactList *list) +tp_contact_list_iface_init (EmpathyContactListIface *iface) { - EmpathyTpContactListPriv *priv; - McAccount *account; - - priv = GET_PRIV (list); - - account = mc_account_lookup (unique_name); - if (status != TP_CONN_STATUS_DISCONNECTED || - !empathy_account_equal (account, priv->account) || - !priv->tp_conn) { - g_object_unref (account); - return; - } - - /* We are disconnected, do just like if the connection was destroyed */ - g_signal_handlers_disconnect_by_func (priv->tp_conn, - tp_contact_list_destroy_cb, - list); - tp_contact_list_destroy_cb (DBUS_G_PROXY (priv->tp_conn), list); - - g_object_unref (account); + iface->add = tp_contact_list_add; + iface->remove = tp_contact_list_remove; + iface->get_members = tp_contact_list_get_members; + iface->get_pendings = tp_contact_list_get_pendings; + iface->get_all_groups = tp_contact_list_get_all_groups; + iface->get_groups = tp_contact_list_get_groups; + iface->add_to_group = tp_contact_list_add_to_group; + iface->remove_from_group = tp_contact_list_remove_from_group; + iface->rename_group = tp_contact_list_rename_group; } diff --git a/libempathy/empathy-tp-contact-list.h b/libempathy/empathy-tp-contact-list.h index f0eccb375..6ac4662fa 100644 --- a/libempathy/empathy-tp-contact-list.h +++ b/libempathy/empathy-tp-contact-list.h @@ -50,20 +50,9 @@ struct _EmpathyTpContactListClass { GObjectClass parent_class; }; -GType empathy_tp_contact_list_get_type (void) G_GNUC_CONST; -EmpathyTpContactList * empathy_tp_contact_list_new (McAccount *account); -McAccount * empathy_tp_contact_list_get_account (EmpathyTpContactList *list); -EmpathyContact * empathy_tp_contact_list_get_user (EmpathyTpContactList *list); -EmpathyContact * empathy_tp_contact_list_get_from_id (EmpathyTpContactList *list, - const gchar *id); -EmpathyContact * empathy_tp_contact_list_get_from_handle (EmpathyTpContactList *list, - guint handle); -GList * empathy_tp_contact_list_get_from_handles (EmpathyTpContactList *list, - GArray *handles); -void empathy_tp_contact_list_rename_group (EmpathyTpContactList *list, - const gchar *old_group, - const gchar *new_group); -GList * empathy_tp_contact_list_get_groups (EmpathyTpContactList *list); +GType empathy_tp_contact_list_get_type (void) G_GNUC_CONST; +EmpathyTpContactList * empathy_tp_contact_list_new (McAccount *account); +McAccount * empathy_tp_contact_list_get_account (EmpathyTpContactList *list); G_END_DECLS diff --git a/libempathy/empathy-tp-group.c b/libempathy/empathy-tp-group.c index b86a00e28..1ffab9ecd 100644 --- a/libempathy/empathy-tp-group.c +++ b/libempathy/empathy-tp-group.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2006 Xavier Claessens <xclaesse@gmail.com> + * Copyright (C) 2007 Collabora Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -16,18 +17,22 @@ * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> */ #include <config.h> #include <dbus/dbus-glib.h> #include <libtelepathy/tp-chan.h> +#include <libtelepathy/tp-chan-gen.h> #include <libtelepathy/tp-chan-iface-group-gen.h> #include <libtelepathy/tp-constants.h> #include <libtelepathy/tp-conn.h> -#include "empathy-debug.h" #include "empathy-tp-group.h" +#include "empathy-contact-factory.h" +#include "empathy-debug.h" #include "empathy-utils.h" #include "empathy-marshal.h" @@ -37,32 +42,27 @@ #define DEBUG_DOMAIN "TpGroup" struct _EmpathyTpGroupPriv { - McAccount *account; - DBusGProxy *group_iface; - TpChan *tp_chan; - gchar *group_name; + EmpathyContactFactory *factory; + McAccount *account; + DBusGProxy *group_iface; + TpChan *tp_chan; + gchar *group_name; + guint self_handle; + + GList *members; + GList *local_pendings; + GList *remote_pendings; }; static void empathy_tp_group_class_init (EmpathyTpGroupClass *klass); static void empathy_tp_group_init (EmpathyTpGroup *group); -static void tp_group_finalize (GObject *object); -static void tp_group_destroy_cb (DBusGProxy *proxy, - EmpathyTpGroup *group); -static void tp_group_members_changed_cb (DBusGProxy *group_iface, - gchar *message, - GArray *added, - GArray *removed, - GArray *local_pending, - GArray *remote_pending, - guint actor, - guint reason, - EmpathyTpGroup *group); enum { - MEMBERS_ADDED, - MEMBERS_REMOVED, + MEMBER_ADDED, + MEMBER_REMOVED, LOCAL_PENDING, REMOTE_PENDING, + DESTROY, LAST_SIGNAL }; @@ -70,6 +70,409 @@ static guint signals[LAST_SIGNAL]; G_DEFINE_TYPE (EmpathyTpGroup, empathy_tp_group, G_TYPE_OBJECT); +static EmpathyContact * +tp_group_get_contact (EmpathyTpGroup *group, + guint handle) +{ + EmpathyTpGroupPriv *priv = GET_PRIV (group); + EmpathyContact *contact = NULL; + + if (handle != 0) { + contact = empathy_contact_factory_get_from_handle (priv->factory, + priv->account, + handle); + } + + if (contact && empathy_contact_get_handle (contact) == priv->self_handle) { + empathy_contact_set_is_user (contact, TRUE); + } + + return contact; +} + +static GList * +tp_group_get_contacts (EmpathyTpGroup *group, + GArray *handles) +{ + EmpathyTpGroupPriv *priv = GET_PRIV (group); + GList *contacts, *l; + + if (!handles) { + return NULL; + } + + contacts = empathy_contact_factory_get_from_handles (priv->factory, + priv->account, + handles); + + /* FIXME: Only useful if the group has a different self handle than + * the connection, otherwise the contact factory already set that + * property. That can be known using group flags. */ + for (l = contacts; l; l = l->next) { + if (empathy_contact_get_handle (l->data) == priv->self_handle) { + empathy_contact_set_is_user (l->data, TRUE); + } + } + + return contacts; +} + +EmpathyPendingInfo * +empathy_pending_info_new (EmpathyContact *member, + EmpathyContact *actor, + const gchar *message) +{ + EmpathyPendingInfo *info; + + info = g_slice_new0 (EmpathyPendingInfo); + + if (member) { + info->member = g_object_ref (member); + } + if (actor) { + info->actor = g_object_ref (actor); + } + if (message) { + info->message = g_strdup (message); + } + + return info; +} + +void +empathy_pending_info_free (EmpathyPendingInfo *info) +{ + if (!info) { + return; + } + + if (info->member) { + g_object_unref (info->member); + } + if (info->actor) { + g_object_unref (info->actor); + } + g_free (info->message); + + g_slice_free (EmpathyPendingInfo, info); +} + +static gint +tp_group_local_pending_find (gconstpointer a, + gconstpointer b) +{ + const EmpathyPendingInfo *info = a; + + return (info->member != b); +} + +static void +tp_group_remove_from_pendings (EmpathyTpGroup *group, + EmpathyContact *contact) +{ + EmpathyTpGroupPriv *priv = GET_PRIV (group); + GList *l; + + /* local pending */ + l = g_list_find_custom (priv->local_pendings, + contact, + tp_group_local_pending_find); + if (l) { + empathy_pending_info_free (l->data); + priv->local_pendings = g_list_delete_link (priv->local_pendings, l); + } + + /* remote pending */ + l = g_list_find (priv->remote_pendings, contact); + if (l) { + g_object_unref (l->data); + priv->remote_pendings = g_list_delete_link (priv->remote_pendings, l); + } +} + +static void +tp_group_members_changed_cb (DBusGProxy *group_iface, + const gchar *message, + GArray *added, + GArray *removed, + GArray *local_pending, + GArray *remote_pending, + guint actor, + guint reason, + EmpathyTpGroup *group) +{ + EmpathyTpGroupPriv *priv = GET_PRIV (group); + EmpathyContact *actor_contact = NULL; + GList *contacts, *l, *ll; + + actor_contact = tp_group_get_contact (group, actor); + + empathy_debug (DEBUG_DOMAIN, "Members changed for list %s:\n" + " added-len=%d, current-len=%d\n" + " removed-len=%d\n" + " local-pending-len=%d, current-len=%d\n" + " remote-pending-len=%d, current-len=%d", + empathy_tp_group_get_name (group), + added ? added->len : 0, g_list_length (priv->members), + removed ? removed->len : 0, + local_pending ? local_pending->len : 0, + g_list_length (priv->local_pendings), + remote_pending ? remote_pending->len : 0, + g_list_length (priv->remote_pendings)); + + /* Contacts added */ + contacts = tp_group_get_contacts (group, added); + for (l = contacts; l; l = l->next) { + tp_group_remove_from_pendings (group, l->data); + + /* If the contact is not yet member, add it and emit signal */ + if (!g_list_find (priv->members, l->data)) { + priv->members = g_list_prepend (priv->members, + g_object_ref (l->data)); + g_signal_emit (group, signals[MEMBER_ADDED], 0, + l->data, actor_contact, reason, message); + } + g_object_unref (l->data); + } + g_list_free (contacts); + + /* Contacts removed */ + contacts = tp_group_get_contacts (group, removed); + for (l = contacts; l; l = l->next) { + tp_group_remove_from_pendings (group, l->data); + + /* If the contact is member, remove it and emit signal */ + if ((ll = g_list_find (priv->members, l->data))) { + g_object_unref (ll->data); + priv->members = g_list_delete_link (priv->members, ll); + g_signal_emit (group, signals[MEMBER_REMOVED], 0, + l->data, actor_contact, reason, message); + } + g_object_unref (l->data); + } + g_list_free (contacts); + + /* Contacts local pending */ + contacts = tp_group_get_contacts (group, local_pending); + for (l = contacts; l; l = l->next) { + /* If the contact is not yet local-pending, add it and emit signal */ + if (!g_list_find_custom (priv->members, l->data, + tp_group_local_pending_find)) { + EmpathyPendingInfo *info; + + info = empathy_pending_info_new (l->data, + actor_contact, + message); + + priv->local_pendings = g_list_prepend (priv->local_pendings, info); + g_signal_emit (group, signals[LOCAL_PENDING], 0, + l->data, actor_contact, reason, message); + } + g_object_unref (l->data); + } + g_list_free (contacts); + + /* Contacts remote pending */ + contacts = tp_group_get_contacts (group, remote_pending); + for (l = contacts; l; l = l->next) { + /* If the contact is not yet remote-pending, add it and emit signal */ + if (!g_list_find (priv->remote_pendings, l->data)) { + priv->remote_pendings = g_list_prepend (priv->remote_pendings, + g_object_ref (l->data)); + g_signal_emit (group, signals[REMOTE_PENDING], 0, + l->data, actor_contact, reason, message); + } + g_object_unref (l->data); + } + g_list_free (contacts); + + if (actor_contact) { + g_object_unref (actor_contact); + } + + empathy_debug (DEBUG_DOMAIN, "Members changed done for list %s:\n" + " members-len=%d\n" + " local-pendings-len=%d\n" + " remote-pendings-len=%d", + empathy_tp_group_get_name (group), + g_list_length (priv->members), + g_list_length (priv->local_pendings), + g_list_length (priv->remote_pendings)); +} + +static void +tp_group_destroy_cb (TpChan *tp_chan, + EmpathyTpGroup *group) +{ + EmpathyTpGroupPriv *priv = GET_PRIV (group); + + empathy_debug (DEBUG_DOMAIN, "Account disconnected or CM crashed"); + + g_object_unref (priv->tp_chan); + priv->group_iface = NULL; + priv->tp_chan = NULL; + + g_signal_emit (group, signals[DESTROY], 0); +} + +static void tp_group_closed_cb (DBusGProxy *proxy, + EmpathyTpGroup *group); + +static void +tp_group_disconnect (EmpathyTpGroup *group) +{ + EmpathyTpGroupPriv *priv = GET_PRIV (group); + + if (priv->group_iface) { + dbus_g_proxy_disconnect_signal (priv->group_iface, "MembersChanged", + G_CALLBACK (tp_group_members_changed_cb), + group); + } + if (priv->tp_chan) { + g_signal_handlers_disconnect_by_func (priv->tp_chan, + tp_group_destroy_cb, + group); + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->tp_chan), "Closed", + G_CALLBACK (tp_group_closed_cb), + group); + } +} + +static void +tp_group_closed_cb (DBusGProxy *proxy, + EmpathyTpGroup *group) +{ + tp_group_disconnect (group); + tp_group_destroy_cb (TELEPATHY_CHAN (proxy), group); +} + +static void +tp_group_get_members_cb (DBusGProxy *proxy, + GArray *handles, + GError *error, + gpointer user_data) +{ + EmpathyTpGroup *group = user_data; + EmpathyTpGroupPriv *priv = GET_PRIV (group); + + if (error) { + empathy_debug (DEBUG_DOMAIN, "Failed to get members: %s", + error->message); + return; + } + + tp_group_members_changed_cb (priv->group_iface, + NULL, /* message */ + handles, /* added */ + NULL, /* removed */ + NULL, /* local_pending */ + NULL, /* remote_pending */ + 0, /* actor */ + 0, /* reason */ + group); +} + +static void +tp_group_get_local_pending_cb (DBusGProxy *proxy, + GPtrArray *array, + GError *error, + gpointer user_data) +{ + EmpathyTpGroup *group = user_data; + EmpathyTpGroupPriv *priv = GET_PRIV (group); + GArray *handles; + guint i; + + if (error) { + empathy_debug (DEBUG_DOMAIN, "Failed to get local pendings: %s", + error->message); + return; + } + + handles = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); + for (i = 0; array->len > i; i++) { + GValueArray *pending_struct; + const gchar *message; + guint member_handle; + guint actor_handle; + guint reason; + + pending_struct = g_ptr_array_index (array, i); + member_handle = g_value_get_uint (g_value_array_get_nth (pending_struct, 0)); + actor_handle = g_value_get_uint (g_value_array_get_nth (pending_struct, 1)); + reason = g_value_get_uint (g_value_array_get_nth (pending_struct, 2)); + message = g_value_get_string (g_value_array_get_nth (pending_struct, 3)); + + g_array_insert_val (handles, 0, member_handle); + tp_group_members_changed_cb (priv->group_iface, + message, /* message */ + NULL, /* added */ + NULL, /* removed */ + handles, /* local_pending */ + NULL, /* remote_pending */ + actor_handle, /* actor */ + reason, /* reason */ + group); + } + g_array_free (handles, TRUE); +} + +static void +tp_group_get_remote_pending_cb (DBusGProxy *proxy, + GArray *handles, + GError *error, + gpointer user_data) +{ + EmpathyTpGroup *group = user_data; + EmpathyTpGroupPriv *priv = GET_PRIV (group); + + if (error) { + empathy_debug (DEBUG_DOMAIN, "Failed to get remote pendings: %s", + error->message); + return; + } + + tp_group_members_changed_cb (priv->group_iface, + NULL, /* message */ + NULL, /* added */ + NULL, /* removed */ + NULL, /* local_pending */ + handles, /* remote_pending */ + 0, /* actor */ + 0, /* reason */ + group); +} + +static void +tp_group_finalize (GObject *object) +{ + EmpathyTpGroupPriv *priv = GET_PRIV (object); + + tp_group_disconnect (EMPATHY_TP_GROUP (object)); + + if (priv->tp_chan) { + g_object_unref (priv->tp_chan); + } + if (priv->account) { + g_object_unref (priv->account); + } + if (priv->factory) { + g_object_unref (priv->factory); + } + g_free (priv->group_name); + + g_list_foreach (priv->members, (GFunc) g_object_unref, NULL); + g_list_free (priv->members); + + g_list_foreach (priv->local_pendings, (GFunc) empathy_pending_info_free, NULL); + g_list_free (priv->local_pendings); + + g_list_foreach (priv->remote_pendings, (GFunc) g_object_unref, NULL); + g_list_free (priv->remote_pendings); + + G_OBJECT_CLASS (empathy_tp_group_parent_class)->finalize (object); +} + static void empathy_tp_group_class_init (EmpathyTpGroupClass *klass) { @@ -77,25 +480,25 @@ empathy_tp_group_class_init (EmpathyTpGroupClass *klass) object_class->finalize = tp_group_finalize; - signals[MEMBERS_ADDED] = - g_signal_new ("members-added", + signals[MEMBER_ADDED] = + g_signal_new ("member-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, - empathy_marshal_VOID__POINTER_UINT_UINT_STRING, + empathy_marshal_VOID__OBJECT_OBJECT_UINT_STRING, G_TYPE_NONE, - 4, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING); + 4, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT, G_TYPE_UINT, G_TYPE_STRING); - signals[MEMBERS_REMOVED] = - g_signal_new ("members-removed", + signals[MEMBER_REMOVED] = + g_signal_new ("member-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, - empathy_marshal_VOID__POINTER_UINT_UINT_STRING, + empathy_marshal_VOID__OBJECT_OBJECT_UINT_STRING, G_TYPE_NONE, - 4, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING); + 4, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT, G_TYPE_UINT, G_TYPE_STRING); signals[LOCAL_PENDING] = g_signal_new ("local-pending", @@ -103,9 +506,9 @@ empathy_tp_group_class_init (EmpathyTpGroupClass *klass) G_SIGNAL_RUN_LAST, 0, NULL, NULL, - empathy_marshal_VOID__POINTER_UINT_UINT_STRING, + empathy_marshal_VOID__OBJECT_OBJECT_UINT_STRING, G_TYPE_NONE, - 4, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING); + 4, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT, G_TYPE_UINT, G_TYPE_STRING); signals[REMOTE_PENDING] = g_signal_new ("remote-pending", @@ -113,9 +516,19 @@ empathy_tp_group_class_init (EmpathyTpGroupClass *klass) G_SIGNAL_RUN_LAST, 0, NULL, NULL, - empathy_marshal_VOID__POINTER_UINT_UINT_STRING, + empathy_marshal_VOID__OBJECT_OBJECT_UINT_STRING, G_TYPE_NONE, - 4, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING); + 4, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT, G_TYPE_UINT, G_TYPE_STRING); + + signals[DESTROY] = + g_signal_new ("destroy", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); g_type_class_add_private (object_class, sizeof (EmpathyTpGroupPriv)); } @@ -125,36 +538,6 @@ empathy_tp_group_init (EmpathyTpGroup *group) { } -static void -tp_group_finalize (GObject *object) -{ - EmpathyTpGroupPriv *priv; - - priv = GET_PRIV (object); - - if (priv->group_iface) { - g_signal_handlers_disconnect_by_func (priv->group_iface, - tp_group_destroy_cb, - object); - dbus_g_proxy_disconnect_signal (priv->group_iface, "MembersChanged", - G_CALLBACK (tp_group_members_changed_cb), - object); - g_object_unref (priv->group_iface); - } - - if (priv->account) { - g_object_unref (priv->account); - } - - if (priv->tp_chan) { - g_object_unref (priv->tp_chan); - } - - g_free (priv->group_name); - - G_OBJECT_CLASS (empathy_tp_group_parent_class)->finalize (object); -} - EmpathyTpGroup * empathy_tp_group_new (McAccount *account, TpChan *tp_chan) @@ -162,6 +545,7 @@ empathy_tp_group_new (McAccount *account, EmpathyTpGroup *group; EmpathyTpGroupPriv *priv; DBusGProxy *group_iface; + GError *error; g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL); g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); @@ -175,262 +559,205 @@ empathy_tp_group_new (McAccount *account, priv->account = g_object_ref (account); priv->tp_chan = g_object_ref (tp_chan); - priv->group_iface = g_object_ref (group_iface); + priv->group_iface = group_iface; + priv->factory = empathy_contact_factory_new (); + + if (!tp_chan_iface_group_get_self_handle (priv->group_iface, + &priv->self_handle, + &error)) { + empathy_debug (DEBUG_DOMAIN, + "Failed to get self handle: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } dbus_g_proxy_connect_signal (priv->group_iface, "MembersChanged", G_CALLBACK (tp_group_members_changed_cb), group, NULL); - g_signal_connect (group_iface, "destroy", + dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_chan), "Closed", + G_CALLBACK (tp_group_closed_cb), + group, NULL); + g_signal_connect (priv->tp_chan, "destroy", G_CALLBACK (tp_group_destroy_cb), group); + tp_chan_iface_group_get_members_async (priv->group_iface, + tp_group_get_members_cb, + group); + tp_chan_iface_group_get_local_pending_members_with_info_async (priv->group_iface, + tp_group_get_local_pending_cb, + group); + tp_chan_iface_group_get_remote_pending_members_async (priv->group_iface, + tp_group_get_remote_pending_cb, + group); + return group; } +static void +tp_group_async_cb (DBusGProxy *proxy, GError *error, gpointer user_data) +{ + const gchar *msg = user_data; + + if (error) { + empathy_debug (DEBUG_DOMAIN, "%s: %s", msg, error->message); + } +} + +void +empathy_tp_group_close (EmpathyTpGroup *group) +{ + EmpathyTpGroupPriv *priv = GET_PRIV (group); + + tp_chan_close_async (DBUS_G_PROXY (priv->tp_chan), + tp_group_async_cb, + "Failed to close"); +} + +static GArray * +tp_group_get_handles (GList *contacts) +{ + GArray *handles; + guint length; + GList *l; + + length = g_list_length (contacts); + handles = g_array_sized_new (FALSE, FALSE, sizeof (guint), length); + + for (l = contacts; l; l = l->next) { + guint handle; + + handle = empathy_contact_get_handle (l->data); + g_array_append_val (handles, handle); + } + + return handles; +} + void empathy_tp_group_add_members (EmpathyTpGroup *group, - GArray *handles, + GList *contacts, const gchar *message) { - EmpathyTpGroupPriv *priv; - GError *error = NULL; + EmpathyTpGroupPriv *priv = GET_PRIV (group); + GArray *handles; g_return_if_fail (EMPATHY_IS_TP_GROUP (group)); - g_return_if_fail (handles != NULL); + g_return_if_fail (contacts != NULL); - priv = GET_PRIV (group); + handles = tp_group_get_handles (contacts); + tp_chan_iface_group_add_members_async (priv->group_iface, + handles, + message, + tp_group_async_cb, + "Failed to add members"); - if (!tp_chan_iface_group_add_members (priv->group_iface, - handles, - message, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Failed to add members: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } + g_array_free (handles, TRUE); } void empathy_tp_group_add_member (EmpathyTpGroup *group, - guint handle, + EmpathyContact *contact, const gchar *message) { - GArray *handles; + EmpathyTpGroupPriv *priv = GET_PRIV (group); + GArray *handles; + guint handle; + handle = empathy_contact_get_handle (contact); handles = g_array_new (FALSE, FALSE, sizeof (guint)); g_array_append_val (handles, handle); - empathy_tp_group_add_members (group, handles, message); + tp_chan_iface_group_add_members_async (priv->group_iface, + handles, + message, + tp_group_async_cb, + "Failed to add member"); g_array_free (handles, TRUE); } void empathy_tp_group_remove_members (EmpathyTpGroup *group, - GArray *handles, + GList *contacts, const gchar *message) { - EmpathyTpGroupPriv *priv; - GError *error = NULL; + EmpathyTpGroupPriv *priv = GET_PRIV (group); + GArray *handles; g_return_if_fail (EMPATHY_IS_TP_GROUP (group)); + g_return_if_fail (contacts != NULL); - priv = GET_PRIV (group); + handles = tp_group_get_handles (contacts); + tp_chan_iface_group_remove_members_async (priv->group_iface, + handles, + message, + tp_group_async_cb, + "Failed to remove members"); - if (!tp_chan_iface_group_remove_members (priv->group_iface, - handles, - message, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Failed to remove members: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } + g_array_free (handles, TRUE); } void empathy_tp_group_remove_member (EmpathyTpGroup *group, - guint handle, + EmpathyContact *contact, const gchar *message) { - GArray *handles; - - g_return_if_fail (EMPATHY_IS_TP_GROUP (group)); + EmpathyTpGroupPriv *priv = GET_PRIV (group); + GArray *handles; + guint handle; + handle = empathy_contact_get_handle (contact); handles = g_array_new (FALSE, FALSE, sizeof (guint)); g_array_append_val (handles, handle); - empathy_tp_group_remove_members (group, handles, message); + tp_chan_iface_group_remove_members_async (priv->group_iface, + handles, + message, + tp_group_async_cb, + "Failed to remove member"); g_array_free (handles, TRUE); } -GArray * +GList * empathy_tp_group_get_members (EmpathyTpGroup *group) { - EmpathyTpGroupPriv *priv; - GArray *members; - GError *error = NULL; + EmpathyTpGroupPriv *priv = GET_PRIV (group); - g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), NULL); + g_list_foreach (priv->members, (GFunc) g_object_ref, NULL); - priv = GET_PRIV (group); - - if (!tp_chan_iface_group_get_members (priv->group_iface, - &members, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Couldn't get members: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - return NULL; - } - - return members; -} - -void -empathy_tp_group_get_all_members (EmpathyTpGroup *group, - GArray **members, - GArray **local_pending, - GArray **remote_pending) -{ - EmpathyTpGroupPriv *priv; - GError *error = NULL; - - g_return_if_fail (EMPATHY_IS_TP_GROUP (group)); - - priv = GET_PRIV (group); - - if (!tp_chan_iface_group_get_all_members (priv->group_iface, - members, - local_pending, - remote_pending, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Couldn't get all members: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } + return g_list_copy (priv->members); } GList * -empathy_tp_group_get_local_pending_members_with_info (EmpathyTpGroup *group) -{ - EmpathyTpGroupPriv *priv; - GPtrArray *array; - guint i; - GList *infos = NULL; - GError *error = NULL; - - g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), NULL); - - priv = GET_PRIV (group); - - if (!tp_chan_iface_group_get_local_pending_members_with_info (priv->group_iface, - &array, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "GetLocalPendingMembersWithInfo failed: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - - return NULL; - } - - if (!array) { - /* This happens with butterfly because - * GetLocalPendingMembersWithInfo is not - * implemented */ - return NULL; - } - - for (i = 0; array->len > i; i++) { - GValueArray *pending_struct; - EmpathyTpGroupInfo *info; - const gchar *message; - - info = g_slice_new (EmpathyTpGroupInfo); - - pending_struct = g_ptr_array_index (array, i); - info->member = g_value_get_uint (g_value_array_get_nth (pending_struct, 0)); - info->actor = g_value_get_uint (g_value_array_get_nth (pending_struct, 1)); - info->reason = g_value_get_uint (g_value_array_get_nth (pending_struct, 2)); - message = g_value_get_string (g_value_array_get_nth (pending_struct, 3)); - info->message = g_strdup (message); - g_value_array_free (pending_struct); - - infos = g_list_prepend (infos, info); - } - g_ptr_array_free (array, TRUE); - - return infos; -} - -void -empathy_tp_group_info_list_free (GList *infos) +empathy_tp_group_get_local_pendings (EmpathyTpGroup *group) { - GList *l; + EmpathyTpGroupPriv *priv = GET_PRIV (group); + GList *pendings = NULL, *l; - for (l = infos; l; l = l->next) { - EmpathyTpGroupInfo *info; + for (l = priv->local_pendings; l; l = l->next) { + EmpathyPendingInfo *info; + EmpathyPendingInfo *new_info; info = l->data; - - g_free (info->message); - g_slice_free (EmpathyTpGroupInfo, info); + new_info = empathy_pending_info_new (info->member, + info->actor, + info->message); + pendings = g_list_prepend (pendings, new_info); } - g_list_free (infos); -} - -static void -tp_group_destroy_cb (DBusGProxy *proxy, - EmpathyTpGroup *group) -{ - EmpathyTpGroupPriv *priv; - - priv = GET_PRIV (group); - - g_object_unref (priv->group_iface); - g_object_unref (priv->tp_chan); - priv->group_iface = NULL; - priv->tp_chan = NULL; + return pendings; } -static void -tp_group_members_changed_cb (DBusGProxy *group_iface, - gchar *message, - GArray *added, - GArray *removed, - GArray *local_pending, - GArray *remote_pending, - guint actor, - guint reason, - EmpathyTpGroup *group) +GList * +empathy_tp_group_get_remote_pendings (EmpathyTpGroup *group) { - EmpathyTpGroupPriv *priv; + EmpathyTpGroupPriv *priv = GET_PRIV (group); - priv = GET_PRIV (group); + g_list_foreach (priv->remote_pendings, (GFunc) g_object_ref, NULL); - /* emit signals */ - if (added->len > 0) { - g_signal_emit (group, signals[MEMBERS_ADDED], 0, - added, actor, reason, message); - } - if (removed->len > 0) { - g_signal_emit (group, signals[MEMBERS_REMOVED], 0, - removed, actor, reason, message); - } - if (local_pending->len > 0) { - g_signal_emit (group, signals[LOCAL_PENDING], 0, - local_pending, actor, reason, message); - } - if (remote_pending->len > 0) { - g_signal_emit (group, signals[REMOTE_PENDING], 0, - remote_pending, actor, reason, message); - } + return g_list_copy (priv->remote_pendings); } const gchar * @@ -443,35 +770,21 @@ empathy_tp_group_get_name (EmpathyTpGroup *group) priv = GET_PRIV (group); /* Lazy initialisation */ - if (priv->group_name) { - return priv->group_name; + if (!priv->group_name) { + priv->group_name = empathy_inspect_channel (priv->account, priv->tp_chan); } - priv->group_name = empathy_inspect_channel (priv->account, priv->tp_chan); - return priv->group_name; } -guint -empathy_tp_group_get_self_handle (EmpathyTpGroup *group) +EmpathyContact * +empathy_tp_group_get_self_contact (EmpathyTpGroup *group) { - EmpathyTpGroupPriv *priv; - guint handle; - GError *error = NULL; - - g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), 0 ); - - priv = GET_PRIV (group); + EmpathyTpGroupPriv *priv = GET_PRIV (group); - if (!tp_chan_iface_group_get_self_handle (priv->group_iface, &handle, &error)) { - empathy_debug (DEBUG_DOMAIN, - "Failed to get self handle: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - return 0; - } + g_return_val_if_fail (EMPATHY_IS_TP_GROUP (group), NULL); - return handle; + return tp_group_get_contact (group, priv->self_handle); } const gchar * @@ -488,21 +801,10 @@ empathy_tp_group_get_object_path (EmpathyTpGroup *group) gboolean empathy_tp_group_is_member (EmpathyTpGroup *group, - guint handle) + EmpathyContact *contact) { - GArray *members; - guint i; - gboolean found = FALSE; - - members = empathy_tp_group_get_members (group); - for (i = 0; i < members->len; i++) { - if (g_array_index (members, guint, i) == handle) { - found = TRUE; - break; - } - } - g_array_free (members, TRUE); - - return found; + EmpathyTpGroupPriv *priv = GET_PRIV (group); + + return g_list_find (priv->members, contact) != NULL; } diff --git a/libempathy/empathy-tp-group.h b/libempathy/empathy-tp-group.h index 5ea7bfc71..14cbd4649 100644 --- a/libempathy/empathy-tp-group.h +++ b/libempathy/empathy-tp-group.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2006 Xavier Claessens <xclaesse@gmail.com> + * Copyright (C) 2007 Collabora Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -16,6 +17,8 @@ * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> */ #ifndef __EMPATHY_TP_GROUP_H__ @@ -24,8 +27,11 @@ #include <glib.h> #include <libtelepathy/tp-chan.h> +#include <libtelepathy/tp-constants.h> #include <libmissioncontrol/mc-account.h> +#include "empathy-contact.h" + G_BEGIN_DECLS #define EMPATHY_TYPE_TP_GROUP (empathy_tp_group_get_type ()) @@ -48,39 +54,40 @@ struct _EmpathyTpGroupClass { }; typedef struct { - guint member; - guint actor; - guint reason; - gchar *message; -} EmpathyTpGroupInfo; + EmpathyContact *member; + EmpathyContact *actor; + gchar *message; + guint reason; +} EmpathyPendingInfo; -GType empathy_tp_group_get_type (void) G_GNUC_CONST; -EmpathyTpGroup * empathy_tp_group_new (McAccount *account, - TpChan *tp_chan); -void empathy_tp_group_add_members (EmpathyTpGroup *group, - GArray *handles, - const gchar *message); -void empathy_tp_group_add_member (EmpathyTpGroup *group, - guint handle, - const gchar *message); -void empathy_tp_group_remove_members (EmpathyTpGroup *group, - GArray *handle, - const gchar *message); -void empathy_tp_group_remove_member (EmpathyTpGroup *group, - guint handle, - const gchar *message); -GArray * empathy_tp_group_get_members (EmpathyTpGroup *group); -void empathy_tp_group_get_all_members (EmpathyTpGroup *group, - GArray **members, - GArray **local_pending, - GArray **remote_pending); -GList * empathy_tp_group_get_local_pending_members_with_info (EmpathyTpGroup *group); -void empathy_tp_group_info_list_free (GList *infos); -const gchar * empathy_tp_group_get_name (EmpathyTpGroup *group); -guint empathy_tp_group_get_self_handle (EmpathyTpGroup *group); -const gchar * empathy_tp_group_get_object_path (EmpathyTpGroup *group); -gboolean empathy_tp_group_is_member (EmpathyTpGroup *group, - guint handle); +GType empathy_tp_group_get_type (void) G_GNUC_CONST; +EmpathyTpGroup * empathy_tp_group_new (McAccount *account, + TpChan *tp_chan); +void empathy_tp_group_close (EmpathyTpGroup *group); +void empathy_tp_group_add_members (EmpathyTpGroup *group, + GList *contacts, + const gchar *message); +void empathy_tp_group_add_member (EmpathyTpGroup *group, + EmpathyContact *contact, + const gchar *message); +void empathy_tp_group_remove_members (EmpathyTpGroup *group, + GList *contacts, + const gchar *message); +void empathy_tp_group_remove_member (EmpathyTpGroup *group, + EmpathyContact *contact, + const gchar *message); +GList * empathy_tp_group_get_members (EmpathyTpGroup *group); +GList * empathy_tp_group_get_local_pendings (EmpathyTpGroup *group); +GList * empathy_tp_group_get_remote_pendings (EmpathyTpGroup *group); +const gchar * empathy_tp_group_get_name (EmpathyTpGroup *group); +EmpathyContact * empathy_tp_group_get_self_contact (EmpathyTpGroup *group); +const gchar * empathy_tp_group_get_object_path (EmpathyTpGroup *group); +gboolean empathy_tp_group_is_member (EmpathyTpGroup *group, + EmpathyContact *contact); +EmpathyPendingInfo *empathy_pending_info_new (EmpathyContact *member, + EmpathyContact *actor, + const gchar *message); +void empathy_pending_info_free (EmpathyPendingInfo *info); G_END_DECLS |