EmpathyContactList is now an interface implemented by
authorXavier Claessens <xclaesse@src.gnome.org>
Fri, 18 May 2007 21:50:47 +0000 (21:50 +0000)
committerXavier Claessens <xclaesse@src.gnome.org>
Fri, 18 May 2007 21:50:47 +0000 (21:50 +0000)
* libempathy-gtk/gossip-private-chat.h:
* libempathy-gtk/gossip-contact-list.c:
* libempathy-gtk/gossip-chat.c:
* libempathy-gtk/gossip-chat.h:
* libempathy-gtk/gossip-chat-window.c:
* libempathy-gtk/Makefile.am:
* libempathy-gtk/gossip-chat-view.c:
* libempathy-gtk/gossip-private-chat.c:
* src/empathy-chat-main.c:
* libempathy/gossip-utils.c:
* libempathy/gossip-contact.h:
* libempathy/empathy-tp-chat.c:
* libempathy/gossip-utils.h:
* libempathy/empathy-contact-manager.c:
* libempathy/empathy-contact-manager.h:
* libempathy/empathy-tp-contact-list.c:
* libempathy/empathy-tp-contact-list.h:
* libempathy/empathy-tp-chatroom.c:
* libempathy/empathy-tp-chatroom.h:
* libempathy/empathy-contact-list.c:
* libempathy/empathy-contact-list.h:
* libempathy/Makefile.am:
* libempathy/gossip-contact.c: EmpathyContactList is now an interface
implemented by EmpathyTpContactList, EmpathyContactManager and
EmpathyTpChatroom. GossipContactList use that interface to display a
treeview.

svn path=/trunk/; revision=72

24 files changed:
ChangeLog
libempathy-gtk/Makefile.am
libempathy-gtk/gossip-chat-view.c
libempathy-gtk/gossip-chat-window.c
libempathy-gtk/gossip-chat.c
libempathy-gtk/gossip-chat.h
libempathy-gtk/gossip-contact-list.c
libempathy-gtk/gossip-private-chat.c
libempathy-gtk/gossip-private-chat.h
libempathy/Makefile.am
libempathy/empathy-contact-list.c
libempathy/empathy-contact-list.h
libempathy/empathy-contact-manager.c
libempathy/empathy-contact-manager.h
libempathy/empathy-tp-chat.c
libempathy/empathy-tp-chatroom.c [new file with mode: 0644]
libempathy/empathy-tp-chatroom.h [new file with mode: 0644]
libempathy/empathy-tp-contact-list.c [new file with mode: 0644]
libempathy/empathy-tp-contact-list.h [new file with mode: 0644]
libempathy/gossip-contact.c
libempathy/gossip-contact.h
libempathy/gossip-utils.c
libempathy/gossip-utils.h
src/empathy-chat-main.c

index b308de0..ac68cf8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,32 @@
+2006-05-18  Xavier Claessens  <xclaesse@gmail.com>
+
+       * libempathy-gtk/gossip-private-chat.h:
+       * libempathy-gtk/gossip-contact-list.c:
+       * libempathy-gtk/gossip-chat.c:
+       * libempathy-gtk/gossip-chat.h:
+       * libempathy-gtk/gossip-chat-window.c:
+       * libempathy-gtk/Makefile.am:
+       * libempathy-gtk/gossip-chat-view.c:
+       * libempathy-gtk/gossip-private-chat.c:
+       * src/empathy-chat-main.c:
+       * libempathy/gossip-utils.c:
+       * libempathy/gossip-contact.h:
+       * libempathy/empathy-tp-chat.c:
+       * libempathy/gossip-utils.h:
+       * libempathy/empathy-contact-manager.c:
+       * libempathy/empathy-contact-manager.h:
+       * libempathy/empathy-tp-contact-list.c:
+       * libempathy/empathy-tp-contact-list.h:
+       * libempathy/empathy-tp-chatroom.c:
+       * libempathy/empathy-tp-chatroom.h:
+       * libempathy/empathy-contact-list.c:
+       * libempathy/empathy-contact-list.h:
+       * libempathy/Makefile.am:
+       * libempathy/gossip-contact.c: EmpathyContactList is now an interface
+       implemented by EmpathyTpContactList, EmpathyContactManager and
+       EmpathyTpChatroom. GossipContactList use that interface to display a 
+       treeview.
+
 2006-05-17  Xavier Claessens  <xclaesse@gmail.com>
 
        * libempathy-gtk/gossip-chat.c: Display ChatState.
index 057b80e..6bc6c65 100644 (file)
@@ -22,9 +22,9 @@ libempathy_gtk_la_SOURCES =                                                   \
        gossip-contact-list.c                   gossip-contact-list.h           \
        gossip-preferences.c                    gossip-preferences.h            \
        gossip-theme-manager.c                  gossip-theme-manager.h          \
+       gossip-chat-window.c                    gossip-chat-window.h            \
        gossip-chat.c                           gossip-chat.h                   \
        gossip-chat-view.c                      gossip-chat-view.h              \
-       gossip-chat-window.c                    gossip-chat-window.h            \
        gossip-private-chat.c                   gossip-private-chat.h           \
        gossip-geometry.c                       gossip-geometry.h               \
        gossip-status-presets.c                 gossip-status-presets.h         \
index 046a550..5f8c7d4 100644 (file)
@@ -1065,7 +1065,7 @@ chat_view_maybe_append_fancy_header (GossipChatView *view,
        priv = GET_PRIV (view);
 
        sender = gossip_message_get_sender (msg);
-       my_contact = gossip_get_own_contact_from_contact (sender);
+       my_contact = gossip_contact_get_user (sender);
        name = gossip_contact_get_name (sender);
        from_self = gossip_contact_equal (sender, my_contact);
 
@@ -1189,7 +1189,7 @@ chat_view_append_irc_action (GossipChatView *view,
        gossip_debug (DEBUG_DOMAIN, "Add IRC action");
 
        sender = gossip_message_get_sender (msg);
-       my_contact = gossip_get_own_contact_from_contact (sender);
+       my_contact = gossip_contact_get_user (sender);
        name = gossip_contact_get_name (sender);
 
        /* Skip the "/me ". */
@@ -1239,7 +1239,7 @@ chat_view_append_fancy_action (GossipChatView *view,
        gossip_debug (DEBUG_DOMAIN, "Add fancy action");
 
        sender = gossip_message_get_sender (msg);
-       my_contact = gossip_get_own_contact_from_contact (sender);
+       my_contact = gossip_contact_get_user (sender);
        name = gossip_contact_get_name (sender);
 
        if (gossip_contact_equal (sender, my_contact)) {
@@ -1284,7 +1284,7 @@ chat_view_append_irc_message (GossipChatView *view,
 
        body = gossip_message_get_body (msg);
        sender = gossip_message_get_sender (msg);
-       my_contact = gossip_get_own_contact_from_contact (sender);
+       my_contact = gossip_contact_get_user (sender);
        name = gossip_contact_get_name (sender);
 
        if (gossip_contact_equal (sender, my_contact)) {
@@ -1335,7 +1335,7 @@ chat_view_append_fancy_message (GossipChatView *view,
        priv = GET_PRIV (view);
 
        sender = gossip_message_get_sender (msg);
-       my_contact = gossip_get_own_contact_from_contact (sender);
+       my_contact = gossip_contact_get_user (sender);
 
        if (gossip_contact_equal (sender, my_contact)) {
                tag = "fancy-body-self";
index fb11237..7dfc8e0 100644 (file)
@@ -810,13 +810,16 @@ FIXME:
                                                   window);
 #endif
        } else {
+               GossipPrivateChat  *chat;
                GossipSubscription  subscription;
                GossipContact      *contact;
 
+               chat = GOSSIP_PRIVATE_CHAT (priv->current_chat);
+
                /* Show / Hide widgets */
                gtk_widget_hide (priv->menu_room);
 
-               contact = gossip_chat_get_contact (priv->current_chat);
+               contact = gossip_private_chat_get_contact (chat);
                subscription = gossip_contact_get_subscription (contact);
                if (!(subscription & GOSSIP_SUBSCRIPTION_FROM)) {
                        gtk_widget_show (priv->menu_conv_add_contact);
@@ -872,11 +875,11 @@ chat_window_add_contact_activate_cb (GtkWidget        *menuitem,
                                     GossipChatWindow *window)
 {
        GossipChatWindowPriv *priv;
-       GossipContact        *contact;
+       //GossipContact        *contact;
 
        priv = GET_PRIV (window);
 
-       contact = gossip_chat_get_contact (priv->current_chat);
+       //contact = gossip_chat_get_contact (priv->current_chat);
 
        // FIXME: gossip_add_contact_dialog_show (NULL, contact);
 }
@@ -911,13 +914,13 @@ chat_window_info_activate_cb (GtkWidget        *menuitem,
                              GossipChatWindow *window)
 {
        GossipChatWindowPriv *priv;
-       GossipContact        *contact;
+       //GossipContact        *contact;
 
        priv = GET_PRIV (window);
-
+/*FIXME:
        contact = gossip_chat_get_contact (priv->current_chat);
 
-/*FIXME:       gossip_contact_info_dialog_show (contact,
+       gossip_contact_info_dialog_show (contact,
                                         GTK_WINDOW (priv->dialog));*/
 }
 
@@ -1007,7 +1010,7 @@ chat_window_show_contacts_toggled_cb (GtkWidget        *menuitem,
        g_return_if_fail (priv->current_chat != NULL);
 
        show = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (priv->menu_room_show_contacts));
-       gossip_chat_set_show_contacts (priv->current_chat, show);
+       //gossip_group_chat_set_show_contacts (GOSSIP_GROUP_CHAT (priv->current_chat), show);
 }
 
 static void
index 179f8f4..70d900d 100644 (file)
@@ -73,6 +73,7 @@ struct _GossipChatPriv {
        GSList                *sent_messages;
        gint                   sent_messages_index;
        GList                 *compositors;
+       guint                  scroll_idle_id;
        /* Used to automatically shrink a window that has temporarily
         * grown due to long input. 
         */
@@ -138,6 +139,7 @@ static void             chat_state_changed_cb             (EmpathyTpChat   *tp_c
                                                           GossipContact   *contact,
                                                           TelepathyChannelChatState  state,
                                                           GossipChat      *chat);
+static gboolean         chat_scroll_down_idle_func        (GossipChat      *chat);
 
 enum {
        COMPOSING,
@@ -268,6 +270,51 @@ gossip_chat_init (GossipChat *chat)
                                    "misspelled",
                                    "underline", PANGO_UNDERLINE_ERROR,
                                    NULL);
+
+
+
+       /* Turn off scrolling temporarily */
+       gossip_chat_view_scroll (chat->view, FALSE);
+#if 0
+FIXME:
+       /* Add messages from last conversation */
+       log_manager = gossip_session_get_log_manager (gossip_app_get_session ());
+       messages = gossip_log_get_last_for_contact (log_manager, priv->contact);
+       num_messages  = g_list_length (messages);
+
+       for (l = messages, i = 0; l; l = l->next, i++) {
+               message = l->data;
+
+               if (num_messages - i > 10) {
+                       continue;
+               }
+
+               sender = gossip_message_get_sender (message);
+               if (gossip_contact_equal (priv->own_contact, sender)) {
+                       gossip_chat_view_append_message_from_self (view,
+                                                                  message,
+                                                                  priv->own_contact,
+                                                                  priv->own_avatar);
+               } else {
+                       gossip_chat_view_append_message_from_other (view,
+                                                                   message,
+                                                                   sender,
+                                                                   priv->other_avatar);
+               }
+       }
+
+       g_list_foreach (messages, (GFunc) g_object_unref, NULL);
+       g_list_free (messages);
+#endif
+       /* Turn back on scrolling */
+       gossip_chat_view_scroll (chat->view, TRUE);
+
+       /* Scroll to the most recent messages, we reference the chat
+        * for the duration of the scroll func.
+        */
+       priv->scroll_idle_id = g_idle_add ((GSourceFunc) chat_scroll_down_idle_func, 
+                                          g_object_ref (chat));
+
 }
 
 static void
@@ -295,6 +342,10 @@ chat_finalize (GObject *object)
                g_object_unref (priv->tp_chat);
        }
 
+       if (priv->scroll_idle_id) {
+               g_source_remove (priv->scroll_idle_id);
+       }
+
        g_free (priv->id);
 
        G_OBJECT_CLASS (gossip_chat_parent_class)->finalize (object);
@@ -343,9 +394,12 @@ chat_send (GossipChat  *chat,
                return;
        }
 
+       /* FIXME: add here something to let group/privrate chat handle
+        *        some special messages */
+
        /* FIXME: gossip_app_set_not_away ();*/
 
-       own_contact = gossip_chat_get_own_contact (chat);
+       own_contact = empathy_contact_manager_get_user (priv->manager, chat->account);
        message = gossip_message_new (msg);
        gossip_message_set_sender (message, own_contact);
 
@@ -544,7 +598,7 @@ chat_input_key_press_event_cb (GtkWidget   *widget,
        }
 
        /* Catch enter but not ctrl/shift-enter */
-       if (IS_ENTER (event->keyval) && !(event->state & GDK_SHIFT_MASK)) {
+       if (IS_ENTER (event->keyval) && !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
                GtkTextView *view;
 
                /* This is to make sure that kinput2 gets the enter. And if
@@ -1067,6 +1121,22 @@ chat_state_changed_cb (EmpathyTpChat             *tp_chat,
        }
 }
 
+/* Scroll down after the back-log has been received. */
+static gboolean
+chat_scroll_down_idle_func (GossipChat *chat)
+{
+       GossipChatPriv *priv;
+
+       priv = GET_PRIV (chat);
+
+       gossip_chat_scroll_down (chat);
+       g_object_unref (chat);
+
+       priv->scroll_idle_id = 0;
+
+       return FALSE;
+}
+
 gboolean
 gossip_chat_get_is_command (const gchar *str)
 {
@@ -1144,29 +1214,6 @@ gossip_chat_get_status_icon_name (GossipChat *chat)
        return NULL;
 }
 
-GossipContact *
-gossip_chat_get_contact (GossipChat *chat)
-{
-       g_return_val_if_fail (GOSSIP_IS_CHAT (chat), NULL);
-
-       if (GOSSIP_CHAT_GET_CLASS (chat)->get_contact) {
-               return GOSSIP_CHAT_GET_CLASS (chat)->get_contact (chat);
-       }
-
-       return NULL;
-}
-GossipContact *
-gossip_chat_get_own_contact (GossipChat *chat)
-{
-       GossipChatPriv *priv;
-
-       g_return_val_if_fail (GOSSIP_IS_CHAT (chat), NULL);
-
-       priv = GET_PRIV (chat);
-
-       return empathy_contact_manager_get_own (priv->manager, chat->account);
-}
-
 GtkWidget *
 gossip_chat_get_widget (GossipChat *chat)
 {
@@ -1203,29 +1250,6 @@ gossip_chat_is_connected (GossipChat *chat)
        return (priv->tp_chat != NULL);
 }
 
-gboolean
-gossip_chat_get_show_contacts (GossipChat *chat)
-{
-       g_return_val_if_fail (GOSSIP_IS_CHAT (chat), FALSE);
-
-       if (GOSSIP_CHAT_GET_CLASS (chat)->get_show_contacts) {
-               return GOSSIP_CHAT_GET_CLASS (chat)->get_show_contacts (chat);
-       }
-
-       return FALSE;
-}
-
-void
-gossip_chat_set_show_contacts (GossipChat *chat,
-                              gboolean    show)
-{
-       g_return_if_fail (GOSSIP_IS_CHAT (chat));
-
-       if (GOSSIP_CHAT_GET_CLASS (chat)->set_show_contacts) {
-               GOSSIP_CHAT_GET_CLASS (chat)->set_show_contacts (chat, show);
-       }
-}
-
 void
 gossip_chat_save_geometry (GossipChat *chat,
                           gint        x,
@@ -1461,7 +1485,7 @@ gossip_chat_should_highlight_nick (GossipMessage *message)
                return FALSE;
        }
 
-       my_contact = gossip_get_own_contact_from_contact (gossip_message_get_sender (message));
+       my_contact = gossip_contact_get_user (gossip_message_get_sender (message));
        to = gossip_contact_get_name (my_contact);
        if (!to) {
                return FALSE;
index 8cbcee0..5fedfdd 100644 (file)
@@ -69,23 +69,8 @@ struct _GossipChatClass {
        const gchar *    (*get_name)            (GossipChat  *chat);
        gchar *          (*get_tooltip)         (GossipChat  *chat);
        const gchar *    (*get_status_icon_name)(GossipChat  *chat);
-       GossipContact *  (*get_contact)         (GossipChat  *chat);
        GtkWidget *      (*get_widget)          (GossipChat  *chat);
-
-       gboolean         (*get_show_contacts)   (GossipChat  *chat);
-       void             (*set_show_contacts)   (GossipChat  *chat,
-                                                gboolean     show);
        gboolean         (*is_group_chat)       (GossipChat  *chat);
-       void             (*save_geometry)       (GossipChat  *chat,
-                                                gint         x,
-                                                gint         y,
-                                                gint         w,
-                                                gint         h);
-       void             (*load_geometry)       (GossipChat  *chat,
-                                                gint        *x,
-                                                gint        *y,
-                                                gint        *w,
-                                                gint        *h);
 };
 
 GType             gossip_chat_get_type              (void);
@@ -103,12 +88,7 @@ void              gossip_chat_paste                 (GossipChat       *chat);
 const gchar *     gossip_chat_get_name              (GossipChat       *chat);
 gchar *           gossip_chat_get_tooltip           (GossipChat       *chat);
 const gchar *     gossip_chat_get_status_icon_name  (GossipChat       *chat);
-GossipContact *   gossip_chat_get_contact           (GossipChat       *chat);
-GossipContact *   gossip_chat_get_own_contact       (GossipChat       *chat);
 GtkWidget *       gossip_chat_get_widget            (GossipChat       *chat);
-gboolean          gossip_chat_get_show_contacts     (GossipChat       *chat);
-void              gossip_chat_set_show_contacts     (GossipChat       *chat,
-                                                    gboolean          show);
 gboolean          gossip_chat_is_group_chat         (GossipChat       *chat);
 gboolean          gossip_chat_is_connected          (GossipChat       *chat);
 
index e268e41..e4abccc 100644 (file)
@@ -32,6 +32,7 @@
 #include <libmissioncontrol/mc-account.h>
 #include <libmissioncontrol/mission-control.h>
 
+#include <libempathy/empathy-contact-list.h>
 #include <libempathy/empathy-contact-manager.h>
 #include <libempathy/gossip-debug.h>
 #include <libempathy/gossip-utils.h>
@@ -66,7 +67,7 @@
 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CONTACT_LIST, GossipContactListPriv))
 
 struct _GossipContactListPriv {
-       EmpathyContactManager *manager;
+       EmpathyContactList    *list;
 
        GHashTable            *groups;
 
@@ -125,7 +126,7 @@ static gboolean contact_list_row_separator_func              (GtkTreeModel
                                                              gpointer                data);
 static void     contact_list_contact_update                  (GossipContactList      *list,
                                                              GossipContact          *contact);
-static void     contact_list_contact_added_cb                (EmpathyContactManager  *manager,
+static void     contact_list_contact_added_cb                (EmpathyContactList     *list_iface,
                                                              GossipContact          *contact,
                                                              GossipContactList      *list);
 static void     contact_list_contact_updated_cb              (GossipContact          *contact,
@@ -134,7 +135,7 @@ static void     contact_list_contact_updated_cb              (GossipContact
 static void     contact_list_contact_groups_updated_cb       (GossipContact          *contact,
                                                              GParamSpec             *param,
                                                              GossipContactList      *list);
-static void     contact_list_contact_removed_cb              (EmpathyContactManager  *manager,
+static void     contact_list_contact_removed_cb              (EmpathyContactList     *list_iface,
                                                              GossipContact          *contact,
                                                              GossipContactList      *list);
 static void     contact_list_contact_set_active              (GossipContactList      *list,
@@ -456,14 +457,14 @@ gossip_contact_list_init (GossipContactList *list)
 
        priv = GET_PRIV (list);
 
-       priv->manager = empathy_contact_manager_new ();
+       priv->list = EMPATHY_CONTACT_LIST (empathy_contact_manager_new ());
        priv->is_compact = FALSE;
        priv->show_active = TRUE;
        priv->show_avatars = TRUE;
 
        contact_list_create_model (list);
        contact_list_setup_view (list);
-       empathy_contact_manager_setup (priv->manager);
+       empathy_contact_list_setup (priv->list);
 
        /* Get saved group states. */
        gossip_contact_groups_get_all ();
@@ -488,11 +489,11 @@ gossip_contact_list_init (GossipContactList *list)
                                              NULL, NULL);
 
        /* Signal connection. */
-       g_signal_connect (priv->manager,
+       g_signal_connect (priv->list,
                          "contact-added",
                          G_CALLBACK (contact_list_contact_added_cb),
                          list);
-       g_signal_connect (priv->manager,
+       g_signal_connect (priv->list,
                          "contact-removed",
                          G_CALLBACK (contact_list_contact_removed_cb),
                          list);
@@ -516,13 +517,13 @@ gossip_contact_list_init (GossipContactList *list)
                          GINT_TO_POINTER (FALSE));
 
        /* Add contacts already created */
-       contacts = empathy_contact_manager_get_contacts (priv->manager);
+       contacts = empathy_contact_list_get_contacts (priv->list);
        for (l = contacts; l; l = l->next) {
                GossipContact *contact;
 
                contact = l->data;
 
-               contact_list_contact_added_cb (priv->manager, contact, list);
+               contact_list_contact_added_cb (priv->list, contact, list);
 
                g_object_unref (contact);
        }
@@ -536,9 +537,9 @@ contact_list_finalize (GObject *object)
 
        priv = GET_PRIV (object);
 
-       /* FIXME: disconnect all signals on the manager and contacts */
+       /* FIXME: disconnect all signals on the list and contacts */
 
-       g_object_unref (priv->manager);
+       g_object_unref (priv->list);
        g_object_unref (priv->ui);
        g_object_unref (priv->store);
        g_object_unref (priv->filter);
@@ -780,9 +781,9 @@ contact_list_contact_update (GossipContactList *list,
 }
 
 static void
-contact_list_contact_added_cb (EmpathyContactManager *manager,
-                              GossipContact         *contact,
-                              GossipContactList     *list)
+contact_list_contact_added_cb (EmpathyContactList *list_iface,
+                              GossipContact      *contact,
+                              GossipContactList  *list)
 {
        GossipContactListPriv *priv;
 
@@ -848,9 +849,9 @@ contact_list_contact_updated_cb (GossipContact     *contact,
 }
 
 static void
-contact_list_contact_removed_cb (EmpathyContactManager *manager,
-                                GossipContact         *contact,
-                                GossipContactList     *list)
+contact_list_contact_removed_cb (EmpathyContactList *list_iface,
+                                GossipContact      *contact,
+                                GossipContactList  *list)
 {
        gossip_debug (DEBUG_DOMAIN, "Contact:'%s' removed",
                      gossip_contact_get_name (contact));
@@ -1569,7 +1570,7 @@ contact_list_drag_data_received (GtkWidget         *widget,
                      id);
 
        /* FIXME: This is ambigous, an id can come from multiple accounts */
-       contact = empathy_contact_manager_find (priv->manager, id);
+       contact = empathy_contact_list_find (priv->list, id);
        if (!contact) {
                gossip_debug (DEBUG_DOMAIN, "No contact found associated with drag & drop");
                return;
@@ -2365,7 +2366,7 @@ contact_list_filter_show_group (GossipContactList *list,
         * group should be shown because a contact we want to
         * show exists in it.
         */
-       contacts = empathy_contact_manager_get_contacts (priv->manager);
+       contacts = empathy_contact_list_get_contacts (priv->list);
        for (l = contacts; l && !show_group; l = l->next) {
                if (!gossip_contact_is_in_group (l->data, group)) {
                        continue;
@@ -2702,7 +2703,7 @@ gossip_contact_list_set_show_offline (GossipContactList *list,
        /* Disable temporarily. */
        priv->show_active = FALSE;
 
-       contacts = empathy_contact_manager_get_contacts (priv->manager);
+       contacts = empathy_contact_list_get_contacts (priv->list);
        for (l = contacts; l; l = l->next) {
                GossipContact *contact;
 
index 0c27abf..84cc156 100644 (file)
@@ -55,7 +55,6 @@ struct _GossipPrivateChatPriv {
        GossipContact *contact;
        gchar         *name;
 
-       guint          scroll_idle_id;
        gboolean       is_online;
 
        GtkWidget     *widget;
@@ -77,7 +76,6 @@ static void           private_chat_widget_destroy_cb            (GtkWidget
 static const gchar *  private_chat_get_name                     (GossipChat             *chat);
 static gchar *        private_chat_get_tooltip                  (GossipChat             *chat);
 static const gchar *  private_chat_get_status_icon_name         (GossipChat             *chat);
-static GossipContact *private_chat_get_contact                  (GossipChat             *chat);
 static GtkWidget *    private_chat_get_widget                   (GossipChat             *chat);
 
 G_DEFINE_TYPE (GossipPrivateChat, gossip_private_chat, GOSSIP_TYPE_CHAT);
@@ -93,11 +91,7 @@ gossip_private_chat_class_init (GossipPrivateChatClass *klass)
        chat_class->get_name             = private_chat_get_name;
        chat_class->get_tooltip          = private_chat_get_tooltip;
        chat_class->get_status_icon_name = private_chat_get_status_icon_name;
-       chat_class->get_contact          = private_chat_get_contact;
        chat_class->get_widget           = private_chat_get_widget;
-       chat_class->get_show_contacts    = NULL;
-       chat_class->set_show_contacts    = NULL;
-       chat_class->is_group_chat        = NULL;
 
        g_type_class_add_private (object_class, sizeof (GossipPrivateChatPriv));
 }
@@ -135,10 +129,6 @@ private_chat_finalize (GObject *object)
                g_object_unref (priv->contact);
        }
 
-       if (priv->scroll_idle_id) {
-               g_source_remove (priv->scroll_idle_id);
-       }
-
        g_free (priv->name);
 
        G_OBJECT_CLASS (gossip_private_chat_parent_class)->finalize (object);
@@ -262,18 +252,16 @@ static gchar *
 private_chat_get_tooltip (GossipChat *chat)
 {
        GossipPrivateChatPriv *priv;
-       GossipContact         *contact;
        const gchar           *status;
 
        g_return_val_if_fail (GOSSIP_IS_PRIVATE_CHAT (chat), NULL);
 
        priv = GET_PRIV (chat);
 
-       contact = gossip_chat_get_contact (chat);
-       status = gossip_contact_get_status (contact);
+       status = gossip_contact_get_status (priv->contact);
 
        return g_strdup_printf ("%s\n%s",
-                               gossip_contact_get_id (contact),
+                               gossip_contact_get_id (priv->contact),
                                status);
 }
 
@@ -281,19 +269,16 @@ static const gchar *
 private_chat_get_status_icon_name (GossipChat *chat)
 {
        GossipPrivateChatPriv *priv;
-       GossipContact         *contact;
 
        g_return_val_if_fail (GOSSIP_IS_PRIVATE_CHAT (chat), NULL);
 
        priv = GET_PRIV (chat);
 
-       contact = gossip_chat_get_contact (chat);
-
-       return gossip_icon_name_for_contact (contact);
+       return gossip_icon_name_for_contact (priv->contact);
 }
 
-static GossipContact *
-private_chat_get_contact (GossipChat *chat)
+GossipContact *
+gossip_private_chat_get_contact (GossipPrivateChat *chat)
 {
        GossipPrivateChatPriv *priv;
 
@@ -314,34 +299,12 @@ private_chat_get_widget (GossipChat *chat)
        return priv->widget;
 }
 
-/* Scroll down after the back-log has been received. */
-static gboolean
-private_chat_scroll_down_idle_func (GossipChat *chat)
-{
-       GossipPrivateChatPriv *priv;
-
-       priv = GET_PRIV (chat);
-
-       gossip_chat_scroll_down (chat);
-       g_object_unref (chat);
-
-       priv->scroll_idle_id = 0;
-
-       return FALSE;
-}
-
 static void
 private_chat_setup (GossipPrivateChat *chat,
                    GossipContact     *contact,
                    EmpathyTpChat     *tp_chat)
 {
        GossipPrivateChatPriv *priv;
-       //GossipLogManager      *log_manager;
-       GossipChatView        *view;
-/*     GossipContact         *sender;
-       GossipMessage         *message;
-       GList                 *messages, *l;
-       gint                   num_messages, i;*/
 
        priv = GET_PRIV (chat);
 
@@ -361,51 +324,6 @@ private_chat_setup (GossipPrivateChat *chat,
                          G_CALLBACK (private_chat_contact_presence_updated_cb),
                          chat);
 
-       view = GOSSIP_CHAT (chat)->view;
-
-       /* Turn off scrolling temporarily */
-       gossip_chat_view_scroll (view, FALSE);
-#if 0
-FIXME:
-       /* Add messages from last conversation */
-       log_manager = gossip_session_get_log_manager (gossip_app_get_session ());
-       messages = gossip_log_get_last_for_contact (log_manager, priv->contact);
-       num_messages  = g_list_length (messages);
-
-       for (l = messages, i = 0; l; l = l->next, i++) {
-               message = l->data;
-
-               if (num_messages - i > 10) {
-                       continue;
-               }
-
-               sender = gossip_message_get_sender (message);
-               if (gossip_contact_equal (priv->own_contact, sender)) {
-                       gossip_chat_view_append_message_from_self (view,
-                                                                  message,
-                                                                  priv->own_contact,
-                                                                  priv->own_avatar);
-               } else {
-                       gossip_chat_view_append_message_from_other (view,
-                                                                   message,
-                                                                   sender,
-                                                                   priv->other_avatar);
-               }
-       }
-
-       g_list_foreach (messages, (GFunc) g_object_unref, NULL);
-       g_list_free (messages);
-#endif
-       /* Turn back on scrolling */
-       gossip_chat_view_scroll (view, TRUE);
-
-       /* Scroll to the most recent messages, we reference the chat
-        * for the duration of the scroll func.
-        */
-       priv->scroll_idle_id = g_idle_add ((GSourceFunc) 
-                                          private_chat_scroll_down_idle_func, 
-                                          g_object_ref (chat));
-
        priv->is_online = gossip_contact_is_online (priv->contact);
 }
 
index e370231..c93b239 100644 (file)
@@ -28,6 +28,8 @@
 #ifndef __GOSSIP_PRIVATE_CHAT_H__
 #define __GOSSIP_PRIVATE_CHAT_H__
 
+#include <libtelepathy/tp-chan.h>
+
 #include <libempathy/gossip-contact.h>
 #include <libempathy/gossip-message.h>
 
@@ -55,9 +57,10 @@ struct _GossipPrivateChatClass {
 };
 
 GType               gossip_private_chat_get_type         (void);
-GossipPrivateChat * gossip_private_chat_new              (GossipContact *contact);
-GossipPrivateChat * gossip_private_chat_new_with_channel (GossipContact *contact,
-                                                         TpChan        *tp_chan);
+GossipPrivateChat * gossip_private_chat_new              (GossipContact     *contact);
+GossipPrivateChat * gossip_private_chat_new_with_channel (GossipContact     *contact,
+                                                         TpChan            *tp_chan);
+GossipContact *     gossip_private_chat_get_contact      (GossipPrivateChat *chat);
 
 G_END_DECLS
 
index 4f1f1aa..684d4e6 100644 (file)
@@ -26,7 +26,9 @@ libempathy_la_SOURCES =                                                       \
        gossip-message.c                gossip-message.h                        \
        empathy-contact-list.c          empathy-contact-list.h                  \
        empathy-contact-manager.c       empathy-contact-manager.h               \
+       empathy-tp-contact-list.c       empathy-tp-contact-list.h               \
        empathy-tp-chat.c               empathy-tp-chat.h                       \
+       empathy-tp-chatroom.c           empathy-tp-chatroom.h                   \
        empathy-chandler.c              empathy-chandler.h                      \
        empathy-idle.c                  empathy-idle.h                          \
        empathy-marshal-main.c
index 6966ba2..f763cdb 100644 (file)
@@ -1,6 +1,5 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com>
  * Copyright (C) 2007 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * 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-helpers.h>
-#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 "config.h"
 
 #include "empathy-contact-list.h"
-#include "gossip-telepathy-group.h"
-#include "gossip-debug.h"
-#include "gossip-utils.h"
-
-#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
-                      EMPATHY_TYPE_CONTACT_LIST, EmpathyContactListPriv))
-
-#define DEBUG_DOMAIN "ContactList"
-#define MAX_AVATAR_REQUESTS 10
-
-struct _EmpathyContactListPriv {
-       TpConn               *tp_conn;
-       McAccount            *account;
-       MissionControl       *mc;
-       GossipContact        *own_contact;
-
-       GossipTelepathyGroup *known;
-       GossipTelepathyGroup *publish;
-       GossipTelepathyGroup *subscribe;
-
-       GHashTable           *groups;
-       GHashTable           *contacts;
 
-       DBusGProxy           *aliasing_iface;
-       DBusGProxy           *avatars_iface;
-       DBusGProxy           *presence_iface;
+static void contact_list_base_init (gpointer klass);
 
-       GList                *avatar_requests_queue;
-};
-
-typedef enum {
-       CONTACT_LIST_TYPE_KNOWN,
-       CONTACT_LIST_TYPE_PUBLISH,
-       CONTACT_LIST_TYPE_SUBSCRIBE,
-       CONTACT_LIST_TYPE_UNKNOWN,
-       CONTACT_LIST_TYPE_COUNT
-} ContactListType;
-
-typedef struct {
-       guint  handle;
-       GList *new_groups;
-} ContactListData;
-
-typedef struct {
-       EmpathyContactList *list;
-       guint                 handle;
-} ContactListAvatarRequestData;
-
-typedef struct {
-       EmpathyContactList *list;
-       guint                *handles;
-} ContactListAliasesRequestData;
-
-static void                   empathy_contact_list_class_init       (EmpathyContactListClass       *klass);
-static void                   empathy_contact_list_init             (EmpathyContactList            *list);
-static void                   contact_list_finalize                 (GObject                       *object);
-static void                   contact_list_finalize_proxies         (EmpathyContactList            *list);
-static void                   contact_list_contact_removed_foreach  (guint                          handle,
-                                                                    GossipContact                 *contact,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_destroy_cb               (DBusGProxy                    *proxy,
-                                                                    EmpathyContactList            *list);
-static gboolean               contact_list_find_foreach             (guint                          handle,
-                                                                    GossipContact                 *contact,
-                                                                    gchar                         *id);
-static void                   contact_list_newchannel_cb            (DBusGProxy                    *proxy,
-                                                                    const gchar                   *object_path,
-                                                                    const gchar                   *channel_type,
-                                                                    TelepathyHandleType            handle_type,
-                                                                    guint                          channel_handle,
-                                                                    gboolean                       suppress_handle,
-                                                                    EmpathyContactList            *list);
-static ContactListType        contact_list_get_type                 (EmpathyContactList            *list,
-                                                                    TpChan                        *list_chan);
-static void                   contact_list_contact_added_cb         (GossipTelepathyGroup          *group,
-                                                                    GArray                        *handles,
-                                                                    guint                          actor_handle,
-                                                                    guint                          reason,
-                                                                    const gchar                   *message,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_contact_removed_cb       (GossipTelepathyGroup          *group,
-                                                                    GArray                        *handles,
-                                                                    guint                          actor_handle,
-                                                                    guint                          reason,
-                                                                    const gchar                   *message,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_local_pending_cb         (GossipTelepathyGroup          *group,
-                                                                    GArray                        *handles,
-                                                                    guint                          actor_handle,
-                                                                    guint                          reason,
-                                                                    const gchar                   *message,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_groups_updated_cb        (GossipContact                 *contact,
-                                                                    GParamSpec                    *param,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_subscription_updated_cb  (GossipContact                 *contact,
-                                                                    GParamSpec                    *param,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_name_updated_cb          (GossipContact                 *contact,
-                                                                    GParamSpec                    *param,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_update_groups_foreach    (gchar                         *object_path,
-                                                                    GossipTelepathyGroup          *group,
-                                                                    ContactListData               *data);
-static GossipTelepathyGroup * contact_list_get_group                (EmpathyContactList            *list,
-                                                                    const gchar                   *name);
-static gboolean               contact_list_find_group               (gchar                         *key,
-                                                                    GossipTelepathyGroup          *group,
-                                                                    gchar                         *group_name);
-static void                   contact_list_get_groups_foreach       (gchar                         *key,
-                                                                    GossipTelepathyGroup          *group,
-                                                                    GList                        **groups);
-static void                   contact_list_group_channel_closed_cb  (TpChan                        *channel,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_group_members_added_cb   (GossipTelepathyGroup          *group,
-                                                                    GArray                        *members,
-                                                                    guint                          actor_handle,
-                                                                    guint                          reason,
-                                                                    const gchar                   *message,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_group_members_removed_cb (GossipTelepathyGroup          *group,
-                                                                    GArray                        *members,
-                                                                    guint                          actor_handle,
-                                                                    guint                          reason,
-                                                                    const gchar                   *message,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_get_contacts_foreach     (guint                          handle,
-                                                                    GossipContact                 *contact,
-                                                                    GList                        **contacts);
-static void                   contact_list_get_info                 (EmpathyContactList            *list,
-                                                                    GArray                        *handles);
-static void                   contact_list_request_avatar           (EmpathyContactList            *list,
-                                                                    guint                          handle);
-static void                   contact_list_start_avatar_requests    (EmpathyContactList            *list);
-static void                   contact_list_avatar_update_cb         (DBusGProxy                    *proxy,
-                                                                    guint                          handle,
-                                                                    gchar                         *new_token,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_request_avatar_cb        (DBusGProxy                    *proxy,
-                                                                    GArray                        *avatar_data,
-                                                                    gchar                         *mime_type,
-                                                                    GError                        *error,
-                                                                    ContactListAvatarRequestData  *data);
-static void                   contact_list_aliases_update_cb        (DBusGProxy                    *proxy,
-                                                                    GPtrArray                     *handlers,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_request_aliases_cb       (DBusGProxy                    *proxy,
-                                                                    gchar                        **contact_names,
-                                                                    GError                        *error,
-                                                                    ContactListAliasesRequestData *data);
-static void                   contact_list_presence_update_cb       (DBusGProxy                    *proxy,
-                                                                    GHashTable                    *handle_table,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_parse_presence_foreach   (guint                          handle,
-                                                                    GValueArray                   *presence_struct,
-                                                                    EmpathyContactList            *list);
-static void                   contact_list_presences_table_foreach  (const gchar                   *state_str,
-                                                                    GHashTable                    *presences_table,
-                                                                    GossipPresence               **presence);
-static void                   contact_list_status_changed_cb        (MissionControl                *mc,
-                                                                    TelepathyConnectionStatus      status,
-                                                                    McPresence                     presence,
-                                                                    TelepathyConnectionStatusReason reason,
-                                                                    const gchar                   *unique_name,
-                                                                    EmpathyContactList            *list);
+GType
+empathy_contact_list_get_type (void)
+{
+       static GType type = 0;
 
-enum {
-       CONTACT_ADDED,
-       CONTACT_REMOVED,
-       DESTROY,
-       LAST_SIGNAL
-};
+       if (!type) {
+               static const GTypeInfo type_info = {
+                       sizeof (EmpathyContactListIface),
+                       contact_list_base_init,
+                       NULL,
+               };
 
-static guint signals[LAST_SIGNAL];
-static guint n_avatar_requests = 0;
+               type = g_type_register_static (G_TYPE_INTERFACE,
+                                              "EmpathyContactList",
+                                              &type_info, 0);
+       }
 
-G_DEFINE_TYPE (EmpathyContactList, empathy_contact_list, G_TYPE_OBJECT);
+       return type;
+}
 
 static void
-empathy_contact_list_class_init (EmpathyContactListClass *klass)
+contact_list_base_init (gpointer klass)
 {
-       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       static gboolean initialized = FALSE;
 
-       object_class->finalize = contact_list_finalize;
-
-       signals[CONTACT_ADDED] =
+       if (!initialized) {
                g_signal_new ("contact-added",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
@@ -229,7 +61,6 @@ empathy_contact_list_class_init (EmpathyContactListClass *klass)
                              G_TYPE_NONE,
                              1, GOSSIP_TYPE_CONTACT);
 
-       signals[CONTACT_REMOVED] =
                g_signal_new ("contact-removed",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
@@ -239,1577 +70,66 @@ empathy_contact_list_class_init (EmpathyContactListClass *klass)
                              G_TYPE_NONE,
                              1, GOSSIP_TYPE_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);
-
-       g_type_class_add_private (object_class, sizeof (EmpathyContactListPriv));
-}
-
-static void
-empathy_contact_list_init (EmpathyContactList *list)
-{
-       EmpathyContactListPriv *priv;
-
-       priv = GET_PRIV (list);
-
-       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);
-}
-
-
-static void
-contact_list_finalize (GObject *object)
-{
-       EmpathyContactListPriv *priv;
-       EmpathyContactList     *list;
-
-       list = EMPATHY_CONTACT_LIST (object);
-       priv = GET_PRIV (list);
-
-       gossip_debug (DEBUG_DOMAIN, "finalize: %p", object);
-
-       dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc),
-                                       "AccountStatusChanged",
-                                       G_CALLBACK (contact_list_status_changed_cb),
-                                       list);
-
-       contact_list_finalize_proxies (list);
-
-       if (priv->tp_conn) {
-               g_object_unref (priv->tp_conn);
+               initialized = TRUE;
        }
-
-       if (priv->known) {
-               g_object_unref (priv->known);
-       }
-
-       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->own_contact);
-       g_object_unref (priv->mc);
-       g_hash_table_destroy (priv->groups);
-       g_hash_table_destroy (priv->contacts);
-
-       G_OBJECT_CLASS (empathy_contact_list_parent_class)->finalize (object);
-}
-
-EmpathyContactList *
-empathy_contact_list_new (McAccount *account)
-{
-       EmpathyContactListPriv *priv;
-       EmpathyContactList     *list;
-       MissionControl         *mc;
-       guint                   handle;
-       GError                 *error = NULL;
-
-       g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
-
-       mc = gossip_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_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;
-
-       g_signal_connect (priv->tp_conn, "destroy",
-                         G_CALLBACK (contact_list_destroy_cb),
-                         list);
-       dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
-                                    "AccountStatusChanged",
-                                    G_CALLBACK (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 (contact_list_aliases_update_cb),
-                                            list, NULL);
-       }
-
-       if (priv->avatars_iface) {
-               dbus_g_proxy_connect_signal (priv->avatars_iface,
-                                            "AvatarUpdated",
-                                            G_CALLBACK (contact_list_avatar_update_cb),
-                                            list, NULL);
-       }
-
-       if (priv->presence_iface) {
-               dbus_g_proxy_connect_signal (priv->presence_iface,
-                                            "PresenceUpdate",
-                                            G_CALLBACK (contact_list_presence_update_cb),
-                                            list, NULL);
-       }
-
-       /* Get our own handle and contact */
-       if (!tp_conn_get_self_handle (DBUS_G_PROXY (priv->tp_conn),
-                                     &handle, &error)) {
-               gossip_debug (DEBUG_DOMAIN, "GetSelfHandle Error: %s",
-                             error ? error->message : "No error given");
-               g_clear_error (&error);
-       } else {
-               priv->own_contact = empathy_contact_list_get_from_handle (list, handle);
-       }
-
-       return list;
 }
 
 void
 empathy_contact_list_setup (EmpathyContactList *list)
 {
-       EmpathyContactListPriv *priv;
-       GPtrArray                *channels;
-       GError                   *error = NULL;
-       guint                     i;
-
        g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list));
 
-       priv = GET_PRIV (list);
-
-       gossip_debug (DEBUG_DOMAIN, "setup contact list: %p", list);
-
-       dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel",
-                                    G_CALLBACK (contact_list_newchannel_cb),
-                                    list, NULL);
-
-       /* Get existing channels */
-       if (!tp_conn_list_channels (DBUS_G_PROXY (priv->tp_conn),
-                                   &channels,
-                                   &error)) {
-               gossip_debug (DEBUG_DOMAIN,
-                             "Failed to get list of open channels: %s",
-                             error ? error->message : "No error given");
-               g_clear_error (&error);
-               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));
-
-               contact_list_newchannel_cb (DBUS_G_PROXY (priv->tp_conn),
-                                           object_path, chan_iface,
-                                           handle_type, handle,
-                                           FALSE, list);
-
-               g_value_array_free (chan_struct);
+       if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->setup) {
+               EMPATHY_CONTACT_LIST_GET_IFACE (list)->setup (list);
        }
-
-       g_ptr_array_free (channels, TRUE);
-}
-
-McAccount *
-empathy_contact_list_get_account (EmpathyContactList *list)
-{
-       EmpathyContactListPriv *priv;
-
-       g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL);
-
-       priv = GET_PRIV (list);
-
-       return priv->account;
-}
-
-GossipContact *
-empathy_contact_list_get_own (EmpathyContactList *list)
-{
-       EmpathyContactListPriv *priv;
-
-       g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL);
-
-       priv = GET_PRIV (list);
-       
-       return priv->own_contact;
 }
 
 GossipContact *
 empathy_contact_list_find (EmpathyContactList *list,
-                          const gchar        *id)
+                                const gchar             *id)
 {
-       EmpathyContactListPriv *priv;
-       GossipContact            *contact;
-
        g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL);
 
-       priv = GET_PRIV (list);
-
-       contact = g_hash_table_find (priv->contacts,
-                                    (GHRFunc) contact_list_find_foreach,
-                                    (gchar*) id);
+       if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->find) {
+               return EMPATHY_CONTACT_LIST_GET_IFACE (list)->find (list, id);
+       }
 
        return NULL;
 }
 
 void
 empathy_contact_list_add (EmpathyContactList *list,
-                         guint               handle,
+                         GossipContact      *contact,
                          const gchar        *message)
 {
-       EmpathyContactListPriv *priv;
-
        g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list));
 
-       priv = GET_PRIV (list);
-
-       gossip_telepathy_group_add_member (priv->subscribe, handle, message);
-}
-
-void
-empathy_contact_list_remove (EmpathyContactList *list,
-                            guint               handle)
-{
-       EmpathyContactListPriv *priv;
-
-       g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list));
-
-       priv = GET_PRIV (list);
-
-       gossip_telepathy_group_remove_member (priv->subscribe, handle, "");
-       gossip_telepathy_group_remove_member (priv->publish, handle, "");
-       gossip_telepathy_group_remove_member (priv->known, handle, "");
-}
-
-GossipContact *
-empathy_contact_list_get_from_id (EmpathyContactList *list,
-                                 const gchar        *id)
-{
-       EmpathyContactListPriv *priv;
-       GossipContact          *contact;
-       const gchar            *contact_ids[] = {id, NULL};
-       GArray                 *handles;
-       guint                   handle;
-       GError                 *error = NULL;
-
-       g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL);
-       g_return_val_if_fail (id != NULL, NULL);
-       
-       priv = GET_PRIV (list);
-
-       contact = empathy_contact_list_find (list, id);
-       if (contact) {
-               return contact;
+       if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->add) {
+               EMPATHY_CONTACT_LIST_GET_IFACE (list)->add (list, contact, message);
        }
-
-       /* 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)) {
-               gossip_debug (DEBUG_DOMAIN, 
-                             "RequestHandle for %s failed: %s", id,
-                             error ? error->message : "No error given");
-               g_clear_error (&error);
-               return 0;
-       }
-
-       handle = g_array_index(handles, guint, 0);
-       g_array_free (handles, TRUE);
-
-       return empathy_contact_list_get_from_handle (list, handle);
-}
-
-GossipContact *
-empathy_contact_list_get_from_handle (EmpathyContactList *list,
-                                     guint               handle)
-{
-       GossipContact *contact;
-       GArray        *handles;
-       GList         *contacts;
-
-       g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL);
-
-       handles = g_array_new (FALSE, FALSE, sizeof (guint));
-       g_array_append_val (handles, handle);
-
-       contacts = empathy_contact_list_get_from_handles (list, handles);
-       g_array_free (handles, TRUE);
-
-       if (!contacts) {
-               return NULL;
-       }
-
-       contact = contacts->data;
-       g_list_free (contacts);
-
-       return contact;
-}
-
-GList *
-empathy_contact_list_get_from_handles (EmpathyContactList *list,
-                                      GArray             *handles)
-{
-       EmpathyContactListPriv  *priv;
-       gchar                  **handles_names;
-       gchar                  **id;
-       GArray                  *new_handles;
-       GList                   *contacts = NULL;
-       guint                    i;
-       GError                  *error = NULL;
-
-       g_return_val_if_fail (EMPATHY_IS_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++) {
-               GossipContact *contact;
-               guint          handle;
-
-               handle = g_array_index (handles, guint, i);
-               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);
-               }
-       }
-
-       if (new_handles->len == 0) {
-               return contacts;
-       }
-
-       /* 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)) {
-               gossip_debug (DEBUG_DOMAIN, 
-                             "HoldHandles Error: %s",
-                             error ? error->message : "No error given");
-               g_clear_error (&error);
-               g_array_free (new_handles, TRUE);
-               return contacts;
-       }
-
-       /* 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)) {
-               gossip_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++) {
-               GossipContact *contact;
-               guint          handle;
-
-               handle = g_array_index (new_handles, guint, i);
-               contact = g_object_new (GOSSIP_TYPE_CONTACT,
-                                       "account", priv->account,
-                                       "id", *id,
-                                       "handle", handle,
-                                       NULL);
-
-               g_signal_connect (contact, "notify::groups",
-                                 G_CALLBACK (contact_list_groups_updated_cb),
-                                 list);
-               g_signal_connect (contact, "notify::subscription",
-                                 G_CALLBACK (contact_list_subscription_updated_cb),
-                                 list);
-               g_signal_connect (contact, "notify::name",
-                                 G_CALLBACK (contact_list_name_updated_cb),
-                                 list);
-
-               gossip_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));
-       }
-
-       contact_list_get_info (list, new_handles);
-
-       g_array_free (new_handles, TRUE);
-       g_strfreev (handles_names);
-
-       return contacts;
 }
 
 void
-empathy_contact_list_rename_group (EmpathyContactList *list,
-                                  const gchar        *old_group,
-                                  const gchar        *new_group)
+empathy_contact_list_remove (EmpathyContactList *list,
+                            GossipContact      *contact,
+                            const gchar        *message)
 {
-       EmpathyContactListPriv *priv;
-       GossipTelepathyGroup   *group;
-       GArray                 *members;
-
        g_return_if_fail (EMPATHY_IS_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) contact_list_find_group,
-                                  (gchar*) old_group);
-       if (!group) {
-               /* The group doesn't exists on this account */
-               return;
-       }
-
-       gossip_debug (DEBUG_DOMAIN, "rename group %s to %s", group, new_group);
-
-       /* Remove all members from the old group */
-       members = gossip_telepathy_group_get_members (group);
-       gossip_telepathy_group_remove_members (group, members, "");
-       contact_list_group_members_removed_cb (group, members, 
-                                              0, 
-                                              TP_CHANNEL_GROUP_CHANGE_REASON_NONE, 
-                                              NULL, list);
-       g_hash_table_remove (priv->groups,
-                            gossip_telepathy_group_get_object_path (group));
 
-       /* Add all members to the new group */
-       group = contact_list_get_group (list, new_group);
-       if (group) {
-               gossip_telepathy_group_add_members (group, members, "");
+       if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove) {
+               EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove (list, contact, message);
        }
 }
 
-GList *
-empathy_contact_list_get_groups (EmpathyContactList *list)
-{
-       EmpathyContactListPriv *priv;
-       GList                    *groups = NULL;
-
-       g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL);
-
-       priv = GET_PRIV (list);
-
-       g_hash_table_foreach (priv->groups,
-                             (GHFunc) contact_list_get_groups_foreach,
-                             &groups);
-
-       groups = g_list_sort (groups, (GCompareFunc) strcmp);
-
-       return groups;
-}
-
 GList *
 empathy_contact_list_get_contacts (EmpathyContactList *list)
 {
-       EmpathyContactListPriv *priv;
-       GList                    *contacts = NULL;
-
        g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL);
 
-       priv = GET_PRIV (list);
-
-       /* FIXME: we should only return contacts that are in the contact list */
-       g_hash_table_foreach (priv->contacts,
-                             (GHFunc) contact_list_get_contacts_foreach,
-                             &contacts);
-
-       return contacts;
-}
-
-static void
-contact_list_finalize_proxies (EmpathyContactList *list)
-{
-       EmpathyContactListPriv *priv;
-
-       priv = GET_PRIV (list);
-
-       if (priv->tp_conn) {
-               g_signal_handlers_disconnect_by_func (priv->tp_conn,
-                                                     contact_list_destroy_cb,
-                                                     list);
-               dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel",
-                                               G_CALLBACK (contact_list_newchannel_cb),
-                                               list);
-       }
-
-       if (priv->aliasing_iface) {
-               dbus_g_proxy_disconnect_signal (priv->aliasing_iface,
-                                               "AliasesChanged",
-                                               G_CALLBACK (contact_list_aliases_update_cb),
-                                               list);
-       }
-
-       if (priv->avatars_iface) {
-               dbus_g_proxy_disconnect_signal (priv->avatars_iface,
-                                               "AvatarUpdated",
-                                               G_CALLBACK (contact_list_avatar_update_cb),
-                                               list);
-       }
-
-       if (priv->presence_iface) {
-               dbus_g_proxy_disconnect_signal (priv->presence_iface,
-                                               "PresenceUpdate",
-                                               G_CALLBACK (contact_list_presence_update_cb),
-                                               list);
-       }
-}
-
-static void
-contact_list_destroy_cb (DBusGProxy         *proxy,
-                        EmpathyContactList *list)
-{
-       EmpathyContactListPriv *priv;
-
-       priv = GET_PRIV (list);
-
-       gossip_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) 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
-contact_list_contact_removed_foreach (guint                 handle,
-                                     GossipContact        *contact,
-                                     EmpathyContactList *list)
-{
-       g_signal_handlers_disconnect_by_func (contact,
-                                             contact_list_groups_updated_cb,
-                                             list);
-       g_signal_handlers_disconnect_by_func (contact,
-                                             contact_list_subscription_updated_cb,
-                                             list);
-       g_signal_handlers_disconnect_by_func (contact,
-                                             contact_list_name_updated_cb,
-                                             list);
-
-       g_signal_emit (list, signals[CONTACT_REMOVED], 0, contact);
-}
-
-static void
-contact_list_block_contact (EmpathyContactList *list,
-                           GossipContact        *contact)
-{
-       g_signal_handlers_block_by_func (contact,
-                                        contact_list_groups_updated_cb,
-                                        list);
-       g_signal_handlers_block_by_func (contact,
-                                        contact_list_subscription_updated_cb,
-                                        list);
-       g_signal_handlers_block_by_func (contact,
-                                        contact_list_name_updated_cb,
-                                        list);
-}
-
-static void
-contact_list_unblock_contact (EmpathyContactList *list,
-                             GossipContact        *contact)
-{
-       g_signal_handlers_unblock_by_func (contact,
-                                          contact_list_groups_updated_cb,
-                                          list);
-       g_signal_handlers_unblock_by_func (contact,
-                                          contact_list_subscription_updated_cb,
-                                          list);
-       g_signal_handlers_unblock_by_func (contact,
-                                          contact_list_name_updated_cb,
-                                          list);
-}
-
-static gboolean
-contact_list_find_foreach (guint          handle,
-                          GossipContact *contact,
-                          gchar         *id)
-{
-       if (strcmp (gossip_contact_get_id (contact), id) == 0) {
-               return TRUE;
-       }
-
-       return FALSE;
-}
-
-static void
-contact_list_newchannel_cb (DBusGProxy          *proxy,
-                           const gchar         *object_path,
-                           const gchar         *channel_type,
-                           TelepathyHandleType  handle_type,
-                           guint                channel_handle,
-                           gboolean             suppress_handle,
-                           EmpathyContactList  *list)
-{
-       EmpathyContactListPriv *priv;
-       GossipTelepathyGroup   *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) {
-               return;
-       }
-
-       bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (priv->tp_conn));
-       new_chan = tp_chan_new (tp_get_bus (),
-                               bus_name,
-                               object_path,
-                               channel_type, handle_type, channel_handle);
-
-       if (handle_type == TP_HANDLE_TYPE_LIST) {
-               ContactListType list_type;
-
-               list_type = contact_list_get_type (list, new_chan);
-               if (list_type == CONTACT_LIST_TYPE_UNKNOWN) {
-                       gossip_debug (DEBUG_DOMAIN, "Unknown contact list channel");
-                       g_object_unref (new_chan);
-                       return;
-               }
-
-               gossip_debug (DEBUG_DOMAIN, "New contact list channel of type: %d",
-                             list_type);
-
-               group = gossip_telepathy_group_new (new_chan, priv->tp_conn);
-
-               switch (list_type) {
-               case CONTACT_LIST_TYPE_KNOWN:
-                       if (priv->known) {
-                               g_object_unref (priv->known);
-                       }
-                       priv->known = group;
-                       break;
-               case CONTACT_LIST_TYPE_PUBLISH:
-                       if (priv->publish) {
-                               g_object_unref (priv->publish);
-                       }
-                       priv->publish = group;
-                       break;
-               case CONTACT_LIST_TYPE_SUBSCRIBE:
-                       if (priv->subscribe) {
-                               g_object_unref (priv->subscribe);
-                       }
-                       priv->subscribe = group;
-                       break;
-               default:
-                       g_assert_not_reached ();
-               }
-
-               /* Connect and setup the new contact-list group */
-               if (list_type == CONTACT_LIST_TYPE_KNOWN ||
-                   list_type == CONTACT_LIST_TYPE_SUBSCRIBE) {
-                       g_signal_connect (group, "members-added",
-                                         G_CALLBACK (contact_list_contact_added_cb),
-                                         list);
-                       g_signal_connect (group, "members-removed",
-                                         G_CALLBACK (contact_list_contact_removed_cb),
-                                         list);
-
-                       members = gossip_telepathy_group_get_members (group);
-                       contact_list_contact_added_cb (group, members, 0,
-                                                      TP_CHANNEL_GROUP_CHANGE_REASON_NONE,
-                                                      NULL, list);
-                       g_array_free (members, TRUE);
-               }
-               if (list_type == CONTACT_LIST_TYPE_PUBLISH) {
-                       GPtrArray *info;
-                       GArray    *pending; 
-                       guint      i;
-
-                       g_signal_connect (group, "local-pending",
-                                         G_CALLBACK (contact_list_local_pending_cb),
-                                         list);
-
-                       info = gossip_telepathy_group_get_local_pending_members_with_info (group);
-
-                       if (!info) {
-                               /* This happens with butterfly because
-                                * GetLocalPendingMembersWithInfo is not 
-                                * implemented */
-                               g_object_unref (new_chan);
-                               return;
-                       }
-
-                       pending = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
-                       for (i = 0; info->len > i; i++) {
-                               GValueArray *pending_struct;
-                               guint        member;
-                               guint        invitor;
-                               guint        reason;
-                               const gchar *message;
-
-                               pending_struct = g_ptr_array_index (info, i);
-                               member = g_value_get_uint (g_value_array_get_nth (pending_struct, 0));
-                               invitor = 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 (pending, 0, member);
-
-                               contact_list_local_pending_cb (group, pending,
-                                                              invitor,
-                                                              reason,
-                                                              message, list);
-
-                               g_value_array_free (pending_struct);
-                       }
-
-                       g_ptr_array_free (info, TRUE);
-                       g_array_free (pending, TRUE);
-               }
-       }
-       else if (handle_type == TP_HANDLE_TYPE_GROUP) {
-               const gchar *object_path;
-
-               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);
-                       return;
-               }
-
-               group = gossip_telepathy_group_new (new_chan, priv->tp_conn);
-
-               gossip_debug (DEBUG_DOMAIN, "New server-side group channel: %s",
-                             gossip_telepathy_group_get_name (group));
-
-               dbus_g_proxy_connect_signal (DBUS_G_PROXY (new_chan), "Closed",
-                                            G_CALLBACK
-                                            (contact_list_group_channel_closed_cb),
-                                            list, NULL);
-
-               g_hash_table_insert (priv->groups, g_strdup (object_path), group);
-               g_signal_connect (group, "members-added",
-                                 G_CALLBACK (contact_list_group_members_added_cb),
-                                 list);
-               g_signal_connect (group, "members-removed",
-                                 G_CALLBACK (contact_list_group_members_removed_cb),
-                                 list);
-
-               members = gossip_telepathy_group_get_members (group);
-               contact_list_group_members_added_cb (group, members, 0,
-                                                    TP_CHANNEL_GROUP_CHANGE_REASON_NONE,
-                                                    NULL, list);
-               g_array_free (members, TRUE);
-       }
-
-       g_object_unref (new_chan);
-}
-
-static ContactListType
-contact_list_get_type (EmpathyContactList *list,
-                      TpChan             *list_chan)
-{
-       EmpathyContactListPriv  *priv;
-       GArray                  *handles;
-       gchar                  **handle_name;
-       ContactListType          list_type;
-       GError                  *error = NULL;
-
-       priv = GET_PRIV (list);
-
-       handles = g_array_new (FALSE, FALSE, sizeof (guint));
-       g_array_append_val (handles, list_chan->handle);
-
-       if (!tp_conn_inspect_handles (DBUS_G_PROXY (priv->tp_conn),
-                                     TP_HANDLE_TYPE_LIST,
-                                     handles,
-                                     &handle_name,
-                                     &error)) {
-               gossip_debug (DEBUG_DOMAIN, 
-                             "InspectHandle Error: %s",
-                             error ? error->message : "No error given");
-               g_clear_error (&error);
-               g_array_free (handles, TRUE);
-               return CONTACT_LIST_TYPE_UNKNOWN;
-       }
-
-       if (strcmp (*handle_name, "subscribe") == 0) {
-               list_type = CONTACT_LIST_TYPE_SUBSCRIBE;
-       } else if (strcmp (*handle_name, "publish") == 0) {
-               list_type = CONTACT_LIST_TYPE_PUBLISH;
-       } else if (strcmp (*handle_name, "known") == 0) {
-               list_type = CONTACT_LIST_TYPE_KNOWN;
-       } else {
-               list_type = CONTACT_LIST_TYPE_UNKNOWN;
-       }
-
-       g_strfreev (handle_name);
-       g_array_free (handles, TRUE);
-
-       return list_type;
-}
-
-static void
-contact_list_contact_added_cb (GossipTelepathyGroup *group,
-                              GArray               *handles,
-                              guint                 actor_handle,
-                              guint                 reason,
-                              const gchar          *message,
-                              EmpathyContactList   *list)
-{
-       EmpathyContactListPriv *priv;
-       GList                  *added_list, *l;
-
-       priv = GET_PRIV (list);
-
-       added_list = empathy_contact_list_get_from_handles (list, handles);
-
-       for (l = added_list; l; l = l->next) {
-               GossipContact *contact;
-
-               contact = GOSSIP_CONTACT (l->data);
-               contact_list_block_contact (list, contact);
-               gossip_contact_set_subscription (contact, GOSSIP_SUBSCRIPTION_BOTH);
-               contact_list_unblock_contact (list, contact);
-
-               g_signal_emit (list, signals[CONTACT_ADDED], 0, contact);
-
-               g_object_unref (contact);
-       }
-
-       g_list_free (added_list);
-}
-
-static void
-contact_list_contact_removed_cb (GossipTelepathyGroup *group,
-                                GArray               *handles,
-                                guint                 actor_handle,
-                                guint                 reason,
-                                const gchar          *message,
-                                EmpathyContactList   *list)
-{
-       EmpathyContactListPriv *priv;
-       GList                  *removed_list, *l;
-
-       priv = GET_PRIV (list);
-
-       removed_list = empathy_contact_list_get_from_handles (list, handles);
-
-       for (l = removed_list; l; l = l->next) {
-               GossipContact *contact;
-               guint          handle;
-
-               contact = GOSSIP_CONTACT (l->data);
-
-               handle = gossip_contact_get_handle (contact);
-               g_hash_table_remove (priv->contacts, GUINT_TO_POINTER (handle));
-
-               g_signal_emit (list, signals[CONTACT_REMOVED], 0, contact);
-
-               g_object_unref (contact);
+       if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_contacts) {
+               return EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_contacts (list);
        }
 
-       g_list_free (removed_list);
-}
-
-static void
-contact_list_local_pending_cb (GossipTelepathyGroup *group,
-                              GArray               *handles,
-                              guint                 actor_handle,
-                              guint                 reason,
-                              const gchar          *message,
-                              EmpathyContactList   *list)
-{
-       EmpathyContactListPriv *priv;
-       GList                  *pending_list, *l;
-
-       priv = GET_PRIV (list);
-
-       pending_list = empathy_contact_list_get_from_handles (list, handles);
-
-       for (l = pending_list; l; l = l->next) {
-               GossipContact *contact;
-
-               contact = GOSSIP_CONTACT (l->data);
-
-               /* FIXME: Is that the correct way ? */
-               contact_list_block_contact (list, contact);
-               gossip_contact_set_subscription (contact, GOSSIP_SUBSCRIPTION_FROM);
-               contact_list_unblock_contact (list, contact);
-               g_signal_emit (list, signals[CONTACT_ADDED], 0, contact);
-
-               g_object_unref (contact);
-       }
-
-       g_list_free (pending_list);
-}
-
-static void
-contact_list_groups_updated_cb (GossipContact      *contact,
-                               GParamSpec         *param,
-                               EmpathyContactList *list)
-{
-       EmpathyContactListPriv *priv;
-       ContactListData         data;
-       GList                  *groups, *l;
-
-       priv = GET_PRIV (list);
-
-       /* Make sure all groups are created */
-       groups = gossip_contact_get_groups (contact);
-       for (l = groups; l; l = l->next) {
-               contact_list_get_group (list, l->data);
-       }
-
-       data.handle = gossip_contact_get_handle (contact);
-       data.new_groups = groups;
-
-       g_hash_table_foreach (priv->groups,
-                             (GHFunc) contact_list_update_groups_foreach,
-                             &data);
-}
-
-static void
-contact_list_subscription_updated_cb (GossipContact      *contact,
-                                     GParamSpec         *param,
-                                     EmpathyContactList *list)
-{
-       EmpathyContactListPriv *priv;
-       GossipSubscription      subscription;
-       guint                   handle;
-
-       priv = GET_PRIV (list);
-
-       subscription = gossip_contact_get_subscription (contact);
-       handle = gossip_contact_get_handle (contact);
-
-       /* FIXME: what to do here, I'm a bit lost... */
-       if (subscription) {
-               gossip_telepathy_group_add_member (priv->publish, handle, "");
-       } else {
-               gossip_telepathy_group_remove_member (priv->publish, handle, "");
-       }
-}
-
-static void
-contact_list_name_updated_cb (GossipContact      *contact,
-                             GParamSpec         *param,
-                             EmpathyContactList *list)
-{
-       EmpathyContactListPriv *priv;
-       GHashTable             *new_alias;
-       const gchar            *new_name;
-       guint                   handle;
-       GError                 *error = NULL;
-
-       priv = GET_PRIV (list);
-       
-       handle = gossip_contact_get_handle (contact);
-       new_name = gossip_contact_get_name (contact);
-
-       gossip_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));
-
-       if (!tp_conn_iface_aliasing_set_aliases (priv->aliasing_iface,
-                                                new_alias,
-                                                &error)) {
-               gossip_debug (DEBUG_DOMAIN, 
-                             "Couldn't rename contact: %s",
-                             error ? error->message : "No error given");
-               g_clear_error (&error);
-       }
-
-       g_hash_table_destroy (new_alias);
-}
-
-static void
-contact_list_update_groups_foreach (gchar                *object_path,
-                                   GossipTelepathyGroup *group,
-                                   ContactListData      *data)
-{
-       gboolean     is_member;
-       gboolean     found = FALSE;
-       const gchar *group_name;
-       GList       *l;
-
-       is_member = gossip_telepathy_group_is_member (group, data->handle);
-       group_name = gossip_telepathy_group_get_name (group);
-
-       for (l = data->new_groups; l; l = l->next) {
-               if (strcmp (group_name, l->data) == 0) {
-                       found = TRUE;
-                       break;
-               }
-       }
-
-       if (is_member && !found) {
-               /* We are no longer member of this group */
-               gossip_debug (DEBUG_DOMAIN, "Contact %d removed from group '%s'",
-                             data->handle, group_name);
-               gossip_telepathy_group_remove_member (group, data->handle, "");
-       }
-
-       if (!is_member && found) {
-               /* We are now member of this group */
-               gossip_debug (DEBUG_DOMAIN, "Contact %d added to group '%s'",
-                             data->handle, group_name);
-               gossip_telepathy_group_add_member (group, data->handle, "");
-       }
-}
-
-static GossipTelepathyGroup *
-contact_list_get_group (EmpathyContactList *list,
-                       const gchar        *name)
-{
-       EmpathyContactListPriv *priv;
-       GossipTelepathyGroup   *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);
-
-       group = g_hash_table_find (priv->groups,
-                                  (GHRFunc) contact_list_find_group,
-                                  (gchar*) name);
-       if (group) {
-               return group;
-       }
-
-       gossip_debug (DEBUG_DOMAIN, "creating new group: %s", name);
-
-       if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn),
-                                     TP_HANDLE_TYPE_GROUP,
-                                     names,
-                                     &handles,
-                                     &error)) {
-               gossip_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;
-       }
-       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)) {
-               gossip_debug (DEBUG_DOMAIN,
-                             "Couldn't request the creation of a new group channel: %s",
-                             error ? error->message : "No error given");
-               g_clear_error (&error);
-               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
-                                    (contact_list_group_channel_closed_cb),
-                                    list,
-                                    NULL);
-
-       group = gossip_telepathy_group_new (group_channel, priv->tp_conn);
-       g_hash_table_insert (priv->groups, group_object_path, group);
-       g_signal_connect (group, "members-added",
-                         G_CALLBACK (contact_list_group_members_added_cb),
-                         list);
-       g_signal_connect (group, "members-removed",
-                         G_CALLBACK (contact_list_group_members_removed_cb),
-                         list);
-
-       return group;
-}
-
-static gboolean
-contact_list_find_group (gchar                 *key,
-                        GossipTelepathyGroup  *group,
-                        gchar                 *group_name)
-{
-       if (strcmp (group_name, gossip_telepathy_group_get_name (group)) == 0) {
-               return TRUE;
-       }
-
-       return FALSE;
-}
-
-static void
-contact_list_get_groups_foreach (gchar                 *key,
-                                GossipTelepathyGroup  *group,
-                                GList                **groups)
-{
-       const gchar *name;
-
-       name = gossip_telepathy_group_get_name (group);
-       *groups = g_list_append (*groups, g_strdup (name));
-}
-
-static void
-contact_list_group_channel_closed_cb (TpChan             *channel,
-                                     EmpathyContactList *list)
-{
-       EmpathyContactListPriv *priv;
-
-       priv = GET_PRIV (list);
-
-       g_hash_table_remove (priv->groups,
-                            dbus_g_proxy_get_path (DBUS_G_PROXY (channel)));
-}
-
-static void
-contact_list_group_members_added_cb (GossipTelepathyGroup *group,
-                                    GArray               *members,
-                                    guint                 actor_handle,
-                                    guint                 reason,
-                                    const gchar          *message,
-                                    EmpathyContactList   *list)
-{
-       EmpathyContactListPriv *priv;
-       GList                  *added_list, *l;
-       const gchar            *group_name;
-
-       priv = GET_PRIV (list);
-
-       group_name = gossip_telepathy_group_get_name (group);
-       added_list = empathy_contact_list_get_from_handles (list, members);
-
-       for (l = added_list; l; l = l->next) {
-               GossipContact *contact;
-               GList         *contact_groups;
-
-               contact = GOSSIP_CONTACT (l->data);
-               contact_groups = gossip_contact_get_groups (contact);
-
-               if (!g_list_find_custom (contact_groups,
-                                        group_name,
-                                        (GCompareFunc) strcmp)) {
-                       gossip_debug (DEBUG_DOMAIN, "Contact %s added to group '%s'",
-                                     gossip_contact_get_name (contact),
-                                     group_name);
-                       contact_groups = g_list_append (contact_groups,
-                                                       g_strdup (group_name));
-                       contact_list_block_contact (list, contact);
-                       gossip_contact_set_groups (contact, contact_groups);
-                       contact_list_unblock_contact (list, contact);
-               }
-
-               g_object_unref (contact);
-       }
-
-       g_list_free (added_list);
-}
-
-static void
-contact_list_group_members_removed_cb (GossipTelepathyGroup *group,
-                                      GArray               *members,
-                                      guint                 actor_handle,
-                                      guint                 reason,
-                                      const gchar          *message,
-                                      EmpathyContactList   *list)
-{
-       EmpathyContactListPriv *priv;
-       GList                  *removed_list, *l;
-       const gchar            *group_name;
-
-       priv = GET_PRIV (list);
-
-       group_name = gossip_telepathy_group_get_name (group);
-       removed_list = empathy_contact_list_get_from_handles (list, members);
-
-       for (l = removed_list; l; l = l->next) {
-               GossipContact *contact;
-               GList         *contact_groups;
-               GList         *to_remove;
-
-               /* FIXME: Does it leak ? */
-               contact = GOSSIP_CONTACT (l->data);
-               contact_groups = gossip_contact_get_groups (contact);
-               contact_groups = g_list_copy (contact_groups);
-
-               to_remove = g_list_find_custom (contact_groups,
-                                               group_name,
-                                               (GCompareFunc) strcmp);
-               if (to_remove) {
-                       gossip_debug (DEBUG_DOMAIN, "Contact %d removed from group '%s'",
-                                     gossip_contact_get_handle (contact),
-                                     group_name);
-                       contact_groups = g_list_remove_link (contact_groups,
-                                                            to_remove);
-                       contact_list_block_contact (list, contact);
-                       gossip_contact_set_groups (contact, contact_groups);
-                       contact_list_unblock_contact (list, contact);
-               }
-
-               g_list_free (contact_groups);
-
-               g_object_unref (contact);
-       }
-
-       g_list_free (removed_list);
-}
-
-static void
-contact_list_get_contacts_foreach (guint           handle,
-                                  GossipContact  *contact,
-                                  GList         **contacts)
-{
-       *contacts = g_list_append (*contacts, g_object_ref (contact));
-}
-
-static void
-contact_list_get_info (EmpathyContactList *list,
-                      GArray             *handles)
-{
-       EmpathyContactListPriv *priv;
-       GError                 *error = NULL;
-
-       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)) {
-                       gossip_debug (DEBUG_DOMAIN, 
-                                     "Could not request presences: %s",
-                                     error ? error->message : "No error given");
-                       g_clear_error (&error);
-               }
-       }
-
-       if (priv->aliasing_iface) {
-               ContactListAliasesRequestData *data;
-
-               data = g_slice_new (ContactListAliasesRequestData);
-               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)
-                                                             contact_list_request_aliases_cb,
-                                                             data);
-       }
-
-       if (priv->avatars_iface) {
-               guint i;
-
-               for (i = 0; i < handles->len; i++) {
-                       guint handle;
-
-                       handle = g_array_index (handles, gint, i);
-                       contact_list_request_avatar (list, handle);
-               }
-       }
-}
-
-static void
-contact_list_request_avatar (EmpathyContactList *list,
-                            guint               handle)
-{
-       EmpathyContactListPriv *priv;
-
-       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 */
-       priv->avatar_requests_queue = g_list_append (priv->avatar_requests_queue,
-                                                    GUINT_TO_POINTER (handle));
-       contact_list_start_avatar_requests (list);
-}
-
-static void
-contact_list_start_avatar_requests (EmpathyContactList *list)
-{
-       EmpathyContactListPriv       *priv;
-       ContactListAvatarRequestData *data;
-
-       priv = GET_PRIV (list);
-
-       while (n_avatar_requests <  MAX_AVATAR_REQUESTS &&
-              priv->avatar_requests_queue) {
-               data = g_slice_new (ContactListAvatarRequestData);
-               data->list = list;
-               data->handle = GPOINTER_TO_UINT (priv->avatar_requests_queue->data);
-
-               n_avatar_requests++;
-               priv->avatar_requests_queue = g_list_remove (priv->avatar_requests_queue,
-                                                            priv->avatar_requests_queue->data);
-
-               tp_conn_iface_avatars_request_avatar_async (priv->avatars_iface,
-                                                           data->handle,
-                                                           (tp_conn_iface_avatars_request_avatar_reply)
-                                                           contact_list_request_avatar_cb,
-                                                           data);
-       }
-}
-
-static void
-contact_list_avatar_update_cb (DBusGProxy         *proxy,
-                              guint               handle,
-                              gchar              *new_token,
-                              EmpathyContactList *list)
-{
-       gossip_debug (DEBUG_DOMAIN, "Changing avatar for %d to %s",
-                     handle, new_token);
-
-       contact_list_request_avatar (list, handle);
-}
-
-static void
-contact_list_request_avatar_cb (DBusGProxy                   *proxy,
-                               GArray                       *avatar_data,
-                               gchar                        *mime_type,
-                               GError                       *error,
-                               ContactListAvatarRequestData *data)
-{
-       GossipContact *contact;
-
-       contact = empathy_contact_list_get_from_handle (data->list, data->handle);
-
-       if (error) {
-               gossip_debug (DEBUG_DOMAIN, "Error requesting avatar for %s: %s",
-                             gossip_contact_get_name (contact),
-                             error ? error->message : "No error given");
-       } else {
-               GossipAvatar *avatar;
-
-               avatar = gossip_avatar_new (avatar_data->data,
-                                           avatar_data->len,
-                                           mime_type);
-               contact_list_block_contact (data->list, contact);
-               gossip_contact_set_avatar (contact, avatar);
-               contact_list_unblock_contact (data->list, contact);
-               gossip_avatar_unref (avatar);
-       }
-
-       n_avatar_requests--;
-       contact_list_start_avatar_requests (data->list);
-
-       g_slice_free (ContactListAvatarRequestData, data);
-}
-
-static void
-contact_list_aliases_update_cb (DBusGProxy         *proxy,
-                               GPtrArray          *renamed_handlers,
-                               EmpathyContactList *list)
-{
-       gint i;
-
-       for (i = 0; renamed_handlers->len > i; i++) {
-               guint          handle;
-               const gchar   *alias;
-               GValueArray   *renamed_struct;
-               GossipContact *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));
-
-               if (alias && *alias == '\0') {
-                       alias = NULL;
-               }
-
-               contact = empathy_contact_list_get_from_handle (list, handle);
-               contact_list_block_contact (list, contact);
-               gossip_contact_set_name (contact, alias);
-               contact_list_unblock_contact (list, contact);
-
-               gossip_debug (DEBUG_DOMAIN, "contact %d renamed to %s (update cb)",
-                             handle, alias);
-       }
-}
-
-static void
-contact_list_request_aliases_cb (DBusGProxy                     *proxy,
-                                gchar                         **contact_names,
-                                GError                         *error,
-                                ContactListAliasesRequestData  *data)
-{
-       guint   i = 0;
-       gchar **name;
-
-       for (name = contact_names; *name && !error; name++) {
-               GossipContact *contact;
-
-               contact = empathy_contact_list_get_from_handle (data->list,
-                                                               data->handles[i]);
-               contact_list_block_contact (data->list, contact);
-               gossip_contact_set_name (contact, *name);
-               contact_list_unblock_contact (data->list, contact);
-
-               gossip_debug (DEBUG_DOMAIN, "contact %d renamed to %s (request cb)",
-                             data->handles[i], *name);
-
-               i++;
-       }
-
-       g_free (data->handles);
-       g_slice_free (ContactListAliasesRequestData, data);
-}
-
-static void
-contact_list_presence_update_cb (DBusGProxy         *proxy,
-                                GHashTable         *handle_table,
-                                EmpathyContactList *list)
-{
-       g_hash_table_foreach (handle_table,
-                             (GHFunc) contact_list_parse_presence_foreach,
-                             list);
-}
-
-static void
-contact_list_parse_presence_foreach (guint               handle,
-                                    GValueArray        *presence_struct,
-                                    EmpathyContactList *list)
-{
-       GHashTable     *presences_table;
-       GossipContact  *contact;
-       GossipPresence *presence = NULL;
-
-       contact = empathy_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) contact_list_presences_table_foreach,
-                             &presence);
-
-       gossip_debug (DEBUG_DOMAIN, "Presence changed for %s (%d) to %s (%d)",
-                     gossip_contact_get_name (contact),
-                     handle,
-                     presence ? gossip_presence_get_status (presence) : "unset",
-                     presence ? gossip_presence_get_state (presence) : MC_PRESENCE_UNSET);
-
-       contact_list_block_contact (list, contact);
-       gossip_contact_set_presence (contact, presence);
-       contact_list_unblock_contact (list, contact);
-}
-
-static void
-contact_list_presences_table_foreach (const gchar     *state_str,
-                                     GHashTable      *presences_table,
-                                     GossipPresence **presence)
-{
-       McPresence    state;
-       const GValue *message;
-
-       state = gossip_presence_state_from_str (state_str);
-       if ((state == MC_PRESENCE_UNSET) || (state == MC_PRESENCE_OFFLINE)) {
-               return;
-       }
-
-       if (*presence) {
-               g_object_unref (*presence);
-               *presence = NULL;
-       }
-
-       *presence = gossip_presence_new ();
-       gossip_presence_set_state (*presence, state);
-
-       message = g_hash_table_lookup (presences_table, "message");
-       if (message != NULL) {
-               gossip_presence_set_status (*presence,
-                                           g_value_get_string (message));
-       }
-}
-
-static void
-contact_list_status_changed_cb (MissionControl                  *mc,
-                               TelepathyConnectionStatus        status,
-                               McPresence                       presence,
-                               TelepathyConnectionStatusReason  reason,
-                               const gchar                     *unique_name,
-                               EmpathyContactList              *list)
-{
-       EmpathyContactListPriv *priv;
-       McAccount              *account;
-
-       priv = GET_PRIV (list);
-
-       account = mc_account_lookup (unique_name);
-       if (status != TP_CONN_STATUS_DISCONNECTED ||
-           !gossip_account_equal (account, priv->account)) {
-               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,
-                                             contact_list_destroy_cb,
-                                             list);
-       contact_list_destroy_cb (DBUS_G_PROXY (priv->tp_conn), list);
-
-       g_object_unref (account);
+       return NULL;
 }
 
index 60c56b2..02ec318 100644 (file)
@@ -1,6 +1,5 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com>
  * Copyright (C) 2007 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * 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_LIST_H__
 #define __EMPATHY_CONTACT_LIST_H__
 
-#include <glib.h>
-#include <libmissioncontrol/mc-account.h>
+#include <glib-object.h>
 
 #include "gossip-contact.h"
 
@@ -33,47 +31,41 @@ G_BEGIN_DECLS
 
 #define EMPATHY_TYPE_CONTACT_LIST         (empathy_contact_list_get_type ())
 #define EMPATHY_CONTACT_LIST(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CONTACT_LIST, EmpathyContactList))
-#define EMPATHY_CONTACT_LIST_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CONTACT_LIST, EmpathyContactListClass))
 #define EMPATHY_IS_CONTACT_LIST(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CONTACT_LIST))
-#define EMPATHY_IS_CONTACT_LIST_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CONTACT_LIST))
-#define EMPATHY_CONTACT_LIST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CONTACT_LIST, EmpathyContactListClass))
+#define EMPATHY_CONTACT_LIST_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), EMPATHY_TYPE_CONTACT_LIST, EmpathyContactListIface))
 
 typedef struct _EmpathyContactList      EmpathyContactList;
-typedef struct _EmpathyContactListClass EmpathyContactListClass;
-typedef struct _EmpathyContactListPriv  EmpathyContactListPriv;
+typedef struct _EmpathyContactListIface EmpathyContactListIface;
 
-struct _EmpathyContactList {
-       GObject      parent;
-};
+struct _EmpathyContactListIface {
+       GTypeInterface   base_iface;
 
-struct _EmpathyContactListClass {
-       GObjectClass parent_class;
+       /* VTabled */
+       void            (*setup)        (EmpathyContactList *list);
+       GossipContact * (*find)         (EmpathyContactList *list,
+                                        const gchar        *id);
+       void            (*add)          (EmpathyContactList *list,
+                                        GossipContact      *contact,
+                                        const gchar        *message);
+       void            (*remove)       (EmpathyContactList *list,
+                                        GossipContact      *contact,
+                                        const gchar        *message);
+       GList *         (*get_contacts) (EmpathyContactList *list);
 };
 
-GType                empathy_contact_list_get_type         (void) G_GNUC_CONST;
-EmpathyContactList * empathy_contact_list_new              (McAccount          *account);
-void                 empathy_contact_list_setup            (EmpathyContactList *list);
-McAccount *          empathy_contact_list_get_account      (EmpathyContactList *list);
-GossipContact *      empathy_contact_list_get_own          (EmpathyContactList *list);
-GossipContact *      empathy_contact_list_find             (EmpathyContactList *list,
-                                                           const gchar        *id);
-void                 empathy_contact_list_add              (EmpathyContactList *list,
-                                                           guint               handle,
-                                                           const gchar        *message);
-void                 empathy_contact_list_remove           (EmpathyContactList *list,
-                                                           guint               handle);
-GossipContact *      empathy_contact_list_get_from_id      (EmpathyContactList *list,
-                                                           const gchar        *id);
-GossipContact *      empathy_contact_list_get_from_handle  (EmpathyContactList *list,
-                                                           guint               handle);
-GList *              empathy_contact_list_get_from_handles (EmpathyContactList *list,
-                                                           GArray             *handles);
-void                 empathy_contact_list_rename_group     (EmpathyContactList *list,
-                                                           const gchar        *old_group,
-                                                           const gchar        *new_group);
-GList *              empathy_contact_list_get_groups       (EmpathyContactList *list);
-GList *              empathy_contact_list_get_contacts     (EmpathyContactList *list);
+GType           empathy_contact_list_get_type     (void) G_GNUC_CONST;
+void            empathy_contact_list_setup        (EmpathyContactList *list);
+GossipContact * empathy_contact_list_find         (EmpathyContactList *list,
+                                                  const gchar        *id);
+void            empathy_contact_list_add          (EmpathyContactList *list,
+                                                  GossipContact      *contact,
+                                                  const gchar        *message);
+void            empathy_contact_list_remove       (EmpathyContactList *list,
+                                                  GossipContact      *contact,
+                                                  const gchar        *message);
+GList *         empathy_contact_list_get_contacts (EmpathyContactList *list);
 
 G_END_DECLS
 
 #endif /* __EMPATHY_CONTACT_LIST_H__ */
+
index ebc6b05..a4cae35 100644 (file)
@@ -27,6 +27,7 @@
 #include <libtelepathy/tp-constants.h>
 
 #include "empathy-contact-manager.h"
+#include "empathy-contact-list.h"
 #include "gossip-utils.h"
 #include "gossip-debug.h"
 
@@ -51,50 +52,55 @@ typedef struct {
        const gchar   *id;
 } ContactManagerFindData;
 
-static void     empathy_contact_manager_class_init   (EmpathyContactManagerClass      *klass);
-static void     empathy_contact_manager_init         (EmpathyContactManager           *manager);
-static void     contact_manager_finalize             (GObject                         *object);
-static void     contact_manager_setup_foreach        (McAccount                       *account,
-                                                     EmpathyContactList              *list,
-                                                     EmpathyContactManager           *manager);
-static gboolean contact_manager_find_foreach         (McAccount                       *account,
-                                                     EmpathyContactList              *list,
-                                                     ContactManagerFindData          *data);
-static void     contact_manager_add_account          (EmpathyContactManager           *manager,
-                                                     McAccount                       *account);
-static void     contact_manager_added_cb             (EmpathyContactList              *list,
-                                                     GossipContact                   *contact,
-                                                     EmpathyContactManager           *manager);
-static void     contact_manager_removed_cb           (EmpathyContactList              *list,
-                                                     GossipContact                   *contact,
-                                                     EmpathyContactManager           *manager);
-static void     contact_manager_destroy_cb           (EmpathyContactList              *list,
-                                                     EmpathyContactManager           *manager);
-static void     contact_manager_rename_group_foreach (McAccount                       *account,
-                                                     EmpathyContactList              *list,
-                                                     ContactManagerRenameGroupData   *data);
-static void     contact_manager_get_groups_foreach   (McAccount                       *account,
-                                                     EmpathyContactList              *list,
-                                                     GList                          **all_groups);
-static void     contact_manager_get_contacts_foreach (McAccount                       *account,
-                                                     EmpathyContactList              *list,
-                                                     GList                          **contacts);
-static void     contact_manager_status_changed_cb    (MissionControl                  *mc,
-                                                     TelepathyConnectionStatus        status,
-                                                     McPresence                       presence,
-                                                     TelepathyConnectionStatusReason  reason,
-                                                     const gchar                     *unique_name,
-                                                     EmpathyContactManager           *manager);
-
-enum {
-       CONTACT_ADDED,
-       CONTACT_REMOVED,
-       LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL];
-
-G_DEFINE_TYPE (EmpathyContactManager, empathy_contact_manager, G_TYPE_OBJECT);
+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 GossipContact *contact_manager_find                 (EmpathyContactList              *manager,
+                                                           const gchar                     *id);
+static void           contact_manager_add                  (EmpathyContactList              *manager,
+                                                           GossipContact                   *contact,
+                                                           const gchar                     *message);
+static void           contact_manager_remove               (EmpathyContactList              *manager,
+                                                           GossipContact                   *contact,
+                                                           const gchar                     *message);
+static GList *        contact_manager_get_contacts         (EmpathyContactList              *manager);
+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,
+                                                           GossipContact                   *contact,
+                                                           EmpathyContactManager           *manager);
+static void           contact_manager_removed_cb           (EmpathyTpContactList            *list,
+                                                           GossipContact                   *contact,
+                                                           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_contacts_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);
+
+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)
@@ -103,29 +109,19 @@ empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
 
        object_class->finalize = contact_manager_finalize;
 
-       signals[CONTACT_ADDED] =
-               g_signal_new ("contact-added",
-                             G_TYPE_FROM_CLASS (klass),
-                             G_SIGNAL_RUN_LAST,
-                             0,
-                             NULL, NULL,
-                             g_cclosure_marshal_VOID__OBJECT,
-                             G_TYPE_NONE,
-                             1, GOSSIP_TYPE_CONTACT);
-
-       signals[CONTACT_REMOVED] =
-               g_signal_new ("contact-removed",
-                             G_TYPE_FROM_CLASS (klass),
-                             G_SIGNAL_RUN_LAST,
-                             0,
-                             NULL, NULL,
-                             g_cclosure_marshal_VOID__OBJECT,
-                             G_TYPE_NONE,
-                             1, GOSSIP_TYPE_CONTACT);
-
        g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
 }
 
+static void
+contact_manager_iface_init (EmpathyContactListIface *iface)
+{
+       iface->setup = contact_manager_setup;
+       iface->find = contact_manager_find;
+       iface->add = contact_manager_add;
+       iface->remove = contact_manager_remove;
+       iface->get_contacts = contact_manager_get_contacts;
+}
+
 static void
 empathy_contact_manager_init (EmpathyContactManager *manager)
 {
@@ -185,8 +181,8 @@ empathy_contact_manager_new (void)
        return manager;
 }
 
-void
-empathy_contact_manager_setup (EmpathyContactManager *manager)
+static void
+contact_manager_setup (EmpathyContactList *manager)
 {
        EmpathyContactManagerPriv *priv;
 
@@ -206,131 +202,145 @@ empathy_contact_manager_setup (EmpathyContactManager *manager)
        priv->setup = TRUE;
 }
 
-EmpathyContactList *
-empathy_contact_manager_get_list (EmpathyContactManager *manager,
-                                 McAccount             *account)
+static GossipContact *
+contact_manager_find (EmpathyContactList *manager,
+                     const gchar        *id)
 {
        EmpathyContactManagerPriv *priv;
+       ContactManagerFindData     data;
 
        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);
 
-       return g_hash_table_lookup (priv->lists, account);
+       data.contact = NULL;
+       data.id = id;
+
+       g_hash_table_find (priv->lists,
+                          (GHRFunc) contact_manager_find_foreach,
+                          &data);
+
+       return data.contact;
 }
 
-GossipContact *
-empathy_contact_manager_get_own (EmpathyContactManager *manager,
-                                McAccount             *account)
+static void
+contact_manager_add (EmpathyContactList *manager,
+                    GossipContact      *contact,
+                    const gchar        *message)
 {
        EmpathyContactManagerPriv *priv;
        EmpathyContactList        *list;
+       McAccount                 *account;
 
-       g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
-       g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
+       g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
+       g_return_if_fail (GOSSIP_IS_CONTACT (contact));
 
        priv = GET_PRIV (manager);
 
+       account = gossip_contact_get_account (contact);
        list = g_hash_table_lookup (priv->lists, account);
-       
-       if (!list) {
-               return NULL;
-       }
 
-       return empathy_contact_list_get_own (list);
+       if (list) {
+               empathy_contact_list_add (list, contact, message);
+       }
 }
 
-GossipContact *
-empathy_contact_manager_create (EmpathyContactManager *manager,
-                               McAccount             *account,
-                               const gchar           *id)
+static void
+contact_manager_remove (EmpathyContactList *manager,
+                       GossipContact      *contact,
+                       const gchar        *message)
 {
        EmpathyContactManagerPriv *priv;
        EmpathyContactList        *list;
+       McAccount                 *account;
 
-       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);
+       g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
+       g_return_if_fail (GOSSIP_IS_CONTACT (contact));
 
        priv = GET_PRIV (manager);
 
+       account = gossip_contact_get_account (contact);
        list = g_hash_table_lookup (priv->lists, account);
-       
-       if (!list) {
-               return NULL;
-       }
 
-       return empathy_contact_list_get_from_id (list, id);
+       if (list) {
+               empathy_contact_list_remove (list, contact, message);
+       }
 }
 
-GossipContact *
-empathy_contact_manager_find (EmpathyContactManager *manager,
-                             const gchar           *id)
+static GList *
+contact_manager_get_contacts (EmpathyContactList *manager)
 {
        EmpathyContactManagerPriv *priv;
-       ContactManagerFindData     data;
+       GList                     *contacts = NULL;
 
        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_foreach (priv->lists,
+                             (GHFunc) contact_manager_get_contacts_foreach,
+                             &contacts);
 
-       g_hash_table_find (priv->lists,
-                          (GHRFunc) contact_manager_find_foreach,
-                          &data);
+       return contacts;
+}
 
-       return data.contact;
+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);
+
+       return g_hash_table_lookup (priv->lists, account);
 }
 
-void
-empathy_contact_manager_add (EmpathyContactManager *manager,
-                            GossipContact         *contact,
-                            const gchar           *message)
+GossipContact *
+empathy_contact_manager_get_user (EmpathyContactManager *manager,
+                                 McAccount             *account)
 {
        EmpathyContactManagerPriv *priv;
-       EmpathyContactList        *list;
-       McAccount                 *account;
-       guint                      handle;
+       EmpathyTpContactList        *list;
 
-       g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
-       g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+       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);
 
-       account = gossip_contact_get_account (contact);
-       handle = gossip_contact_get_handle (contact);
        list = g_hash_table_lookup (priv->lists, account);
-
-       if (list) {
-               empathy_contact_list_add (list, handle, message);
+       
+       if (!list) {
+               return NULL;
        }
+
+       return empathy_tp_contact_list_get_user (list);
 }
 
-void
-empathy_contact_manager_remove (EmpathyContactManager *manager,
-                               GossipContact         *contact)
+GossipContact *
+empathy_contact_manager_create (EmpathyContactManager *manager,
+                               McAccount             *account,
+                               const gchar           *id)
 {
        EmpathyContactManagerPriv *priv;
-       EmpathyContactList        *list;
-       McAccount                 *account;
-       guint                      handle;
+       EmpathyTpContactList      *list;
 
-       g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
-       g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+       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);
 
-       account = gossip_contact_get_account (contact);
-       handle = gossip_contact_get_handle (contact);
        list = g_hash_table_lookup (priv->lists, account);
-
-       if (list) {
-               empathy_contact_list_remove (list, handle);
+       
+       if (!list) {
+               return NULL;
        }
+
+       return empathy_tp_contact_list_get_from_id (list, id);
 }
 
 void
@@ -372,37 +382,21 @@ empathy_contact_manager_get_groups (EmpathyContactManager *manager)
        return groups;
 }
 
-GList *
-empathy_contact_manager_get_contacts (EmpathyContactManager *manager)
-{
-       EmpathyContactManagerPriv *priv;
-       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_contacts_foreach,
-                             &contacts);
-
-       return contacts;
-}
-
 static void
 contact_manager_setup_foreach (McAccount             *account,
-                              EmpathyContactList    *list,
+                              EmpathyTpContactList  *list,
                               EmpathyContactManager *manager)
 {
-       empathy_contact_list_setup (list);
+       empathy_contact_list_setup (EMPATHY_CONTACT_LIST (list));
 }
 
 static gboolean
 contact_manager_find_foreach (McAccount              *account,
-                             EmpathyContactList     *list,
+                             EmpathyTpContactList   *list,
                               ContactManagerFindData *data)
 {
-       data->contact = empathy_contact_list_find (list, data->id);
+       data->contact = empathy_contact_list_find (EMPATHY_CONTACT_LIST (list),
+                                                  data->id);
        
        if (data->contact) {
                return TRUE;
@@ -416,7 +410,7 @@ contact_manager_add_account (EmpathyContactManager *manager,
                             McAccount             *account)
 {
        EmpathyContactManagerPriv *priv;
-       EmpathyContactList        *list;
+       EmpathyTpContactList        *list;
 
        priv = GET_PRIV (manager);
 
@@ -427,7 +421,7 @@ contact_manager_add_account (EmpathyContactManager *manager,
        gossip_debug (DEBUG_DOMAIN, "Adding new account: %s",
                      mc_account_get_display_name (account));
 
-       list = empathy_contact_list_new (account);
+       list = empathy_tp_contact_list_new (account);
        if (!list) {
                return;
        }
@@ -446,28 +440,28 @@ contact_manager_add_account (EmpathyContactManager *manager,
                          manager);
 
        if (priv->setup) {
-               empathy_contact_list_setup (list);
+               empathy_contact_list_setup (EMPATHY_CONTACT_LIST (list));
        }
 }
 
 static void
-contact_manager_added_cb (EmpathyContactList    *list,
+contact_manager_added_cb (EmpathyTpContactList  *list,
                          GossipContact         *contact,
                          EmpathyContactManager *manager)
 {
-       g_signal_emit (manager, signals[CONTACT_ADDED], 0, contact);
+       g_signal_emit_by_name (manager, "contact-added", contact);
 }
 
 static void
-contact_manager_removed_cb (EmpathyContactList    *list,
+contact_manager_removed_cb (EmpathyTpContactList  *list,
                            GossipContact         *contact,
                            EmpathyContactManager *manager)
 {
-       g_signal_emit (manager, signals[CONTACT_REMOVED], 0, contact);
+       g_signal_emit_by_name (manager, "contact-removed", contact);
 }
 
 static void
-contact_manager_destroy_cb (EmpathyContactList    *list,
+contact_manager_destroy_cb (EmpathyTpContactList  *list,
                            EmpathyContactManager *manager)
 {
        EmpathyContactManagerPriv *priv;
@@ -475,7 +469,7 @@ contact_manager_destroy_cb (EmpathyContactList    *list,
 
        priv = GET_PRIV (manager);
 
-       account = empathy_contact_list_get_account (list);
+       account = empathy_tp_contact_list_get_account (list);
 
        gossip_debug (DEBUG_DOMAIN, "Removing account: %s",
                      mc_account_get_display_name (account));
@@ -496,22 +490,22 @@ contact_manager_destroy_cb (EmpathyContactList    *list,
 
 static void
 contact_manager_rename_group_foreach (McAccount                     *account,
-                                     EmpathyContactList            *list,
+                                     EmpathyTpContactList          *list,
                                      ContactManagerRenameGroupData *data)
 {
-       empathy_contact_list_rename_group (list,
-                                          data->old_group,
-                                          data->new_group);
+       empathy_tp_contact_list_rename_group (list,
+                                             data->old_group,
+                                             data->new_group);
 }
 
 static void
-contact_manager_get_groups_foreach (McAccount           *account,
-                                   EmpathyContactList  *list,
-                                   GList              **all_groups)
+contact_manager_get_groups_foreach (McAccount             *account,
+                                   EmpathyTpContactList  *list,
+                                   GList                **all_groups)
 {
        GList *groups, *l;
 
-       groups = empathy_contact_list_get_groups (list);
+       groups = empathy_tp_contact_list_get_groups (list);
        for (l = groups; l; l = l->next) {
                if (!g_list_find_custom (*all_groups,
                                         l->data,
@@ -526,13 +520,13 @@ contact_manager_get_groups_foreach (McAccount           *account,
 }
 
 static void
-contact_manager_get_contacts_foreach (McAccount           *account,
-                                     EmpathyContactList  *list,
-                                     GList              **contacts)
+contact_manager_get_contacts_foreach (McAccount             *account,
+                                     EmpathyTpContactList  *list,
+                                     GList                **contacts)
 {
        GList *l;
 
-       l = empathy_contact_list_get_contacts (list);
+       l = empathy_contact_list_get_contacts (EMPATHY_CONTACT_LIST (list));
        *contacts = g_list_concat (*contacts, l);
 }
 
index 95876db..db893b4 100644 (file)
@@ -28,7 +28,7 @@
 #include <libmissioncontrol/mc-account.h>
 
 #include "gossip-contact.h"
-#include "empathy-contact-list.h"
+#include "empathy-tp-contact-list.h"
 
 G_BEGIN_DECLS
 
@@ -53,26 +53,17 @@ struct _EmpathyContactManagerClass {
 
 GType                  empathy_contact_manager_get_type     (void) G_GNUC_CONST;
 EmpathyContactManager *empathy_contact_manager_new          (void);
-void                   empathy_contact_manager_setup        (EmpathyContactManager *manager);
-EmpathyContactList *   empathy_contact_manager_get_list     (EmpathyContactManager *manager,
+EmpathyTpContactList * empathy_contact_manager_get_list     (EmpathyContactManager *manager,
                                                             McAccount             *account);
-GossipContact *        empathy_contact_manager_get_own      (EmpathyContactManager *manager,
+GossipContact *        empathy_contact_manager_get_user     (EmpathyContactManager *manager,
                                                             McAccount             *account);
-GossipContact *        empathy_contact_manager_find         (EmpathyContactManager *manager,
-                                                            const gchar           *id);
 GossipContact *        empathy_contact_manager_create       (EmpathyContactManager *manager,
                                                             McAccount             *account,
                                                             const gchar           *id);
-void                   empathy_contact_manager_add          (EmpathyContactManager *manager,
-                                                            GossipContact         *contact,
-                                                            const gchar           *message);
-void                   empathy_contact_manager_remove       (EmpathyContactManager *manager,
-                                                            GossipContact         *contact);
 void                   empathy_contact_manager_rename_group (EmpathyContactManager *manager,
                                                             const gchar           *old_group,
                                                             const gchar           *new_group);
 GList *                empathy_contact_manager_get_groups   (EmpathyContactManager *manager);
-GList *                empathy_contact_manager_get_contacts (EmpathyContactManager *manager);
 
 G_END_DECLS
 
index 58950f6..73f6049 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <config.h>
 
+#include <string.h>
+
 #include <libtelepathy/tp-chan-type-text-gen.h>
 #include <libtelepathy/tp-chan-iface-chat-state-gen.h>
 #include <libtelepathy/tp-conn.h>
@@ -29,7 +31,7 @@
 
 #include "empathy-tp-chat.h"
 #include "empathy-contact-manager.h"
-#include "empathy-contact-list.h"
+#include "empathy-tp-contact-list.h"
 #include "empathy-marshal.h"
 #include "gossip-debug.h"
 #include "gossip-time.h"
@@ -41,7 +43,7 @@
 #define DEBUG_DOMAIN "TpChat"
 
 struct _EmpathyTpChatPriv {
-       EmpathyContactList    *list;
+       EmpathyTpContactList  *list;
        EmpathyContactManager *manager;
        McAccount             *account;
        gchar                 *id;
@@ -52,35 +54,51 @@ struct _EmpathyTpChatPriv {
        DBusGProxy            *chat_state_iface;
 };
 
-static void empathy_tp_chat_class_init (EmpathyTpChatClass        *klass);
-static void empathy_tp_chat_init       (EmpathyTpChat             *chat);
-static void tp_chat_finalize           (GObject                   *object);
-static void tp_chat_destroy_cb         (TpChan                    *text_chan,
-                                       EmpathyTpChat             *chat);
-static void tp_chat_closed_cb          (TpChan                    *text_chan,
-                                       EmpathyTpChat             *chat);
-static void tp_chat_received_cb        (DBusGProxy                *text_iface,
-                                       guint                      message_id,
-                                       guint                      timestamp,
-                                       guint                      from_handle,
-                                       guint                      message_type,
-                                       guint                      message_flags,
-                                       gchar                     *message_body,
-                                       EmpathyTpChat             *chat);
-static void tp_chat_sent_cb            (DBusGProxy                *text_iface,
-                                       guint                      timestamp,
-                                       guint                      message_type,
-                                       gchar                     *message_body,
-                                       EmpathyTpChat             *chat);
-static void tp_chat_state_changed_cb   (DBusGProxy                *chat_state_iface,
-                                       guint                      handle,
-                                       TelepathyChannelChatState  state,
-                                       EmpathyTpChat             *chat);
-static void tp_chat_emit_message       (EmpathyTpChat             *chat,
-                                       guint                      type,
-                                       guint                      timestamp,
-                                       guint                      from_handle,
-                                       const gchar               *message_body);
+static void      empathy_tp_chat_class_init (EmpathyTpChatClass        *klass);
+static void      empathy_tp_chat_init       (EmpathyTpChat             *chat);
+static void      tp_chat_finalize           (GObject                   *object);
+static GObject * tp_chat_constructor        (GType                      type,
+                                            guint                      n_props,
+                                            GObjectConstructParam     *props);
+static void      tp_chat_get_property       (GObject                   *object,
+                                            guint                      param_id,
+                                            GValue                    *value,
+                                            GParamSpec                *pspec);
+static void      tp_chat_set_property       (GObject                   *object,
+                                            guint                      param_id,
+                                            const GValue              *value,
+                                            GParamSpec                *pspec);
+static void      tp_chat_destroy_cb         (TpChan                    *text_chan,
+                                            EmpathyTpChat             *chat);
+static void      tp_chat_closed_cb          (TpChan                    *text_chan,
+                                            EmpathyTpChat             *chat);
+static void      tp_chat_received_cb        (DBusGProxy                *text_iface,
+                                            guint                      message_id,
+                                            guint                      timestamp,
+                                            guint                      from_handle,
+                                            guint                      message_type,
+                                            guint                      message_flags,
+                                            gchar                     *message_body,
+                                            EmpathyTpChat             *chat);
+static void      tp_chat_sent_cb            (DBusGProxy                *text_iface,
+                                            guint                      timestamp,
+                                            guint                      message_type,
+                                            gchar                     *message_body,
+                                            EmpathyTpChat             *chat);
+static void      tp_chat_state_changed_cb   (DBusGProxy                *chat_state_iface,
+                                            guint                      handle,
+                                            TelepathyChannelChatState  state,
+                                            EmpathyTpChat             *chat);
+static void      tp_chat_emit_message       (EmpathyTpChat             *chat,
+                                            guint                      type,
+                                            guint                      timestamp,
+                                            guint                      from_handle,
+                                            const gchar               *message_body);
+enum {
+       PROP_0,
+       PROP_ACCOUNT,
+       PROP_TP_CHAN
+};
 
 enum {
        MESSAGE_RECEIVED,
@@ -99,6 +117,27 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
        object_class->finalize = tp_chat_finalize;
+       object_class->constructor = tp_chat_constructor;
+       object_class->get_property = tp_chat_get_property;
+       object_class->set_property = tp_chat_set_property;
+
+       g_object_class_install_property (object_class,
+                                        PROP_ACCOUNT,
+                                        g_param_spec_object ("account",
+                                                             "channel Account",
+                                                             "The account associated with the channel",
+                                                             MC_TYPE_ACCOUNT,
+                                                             G_PARAM_READWRITE |
+                                                             G_PARAM_CONSTRUCT_ONLY));
+
+       g_object_class_install_property (object_class,
+                                        PROP_TP_CHAN,
+                                        g_param_spec_object ("tp-chan",
+                                                             "telepathy channel",
+                                                             "The text channel for the chat",
+                                                             TELEPATHY_CHAN_TYPE,
+                                                             G_PARAM_READWRITE |
+                                                             G_PARAM_CONSTRUCT_ONLY));
 
        signals[MESSAGE_RECEIVED] =
                g_signal_new ("message-received",
@@ -182,29 +221,26 @@ tp_chat_finalize (GObject *object)
        G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
 }
 
-EmpathyTpChat *
-empathy_tp_chat_new (McAccount *account,
-                    TpChan    *tp_chan)
+static GObject *
+tp_chat_constructor (GType                  type,
+                    guint                  n_props,
+                    GObjectConstructParam *props)
 {
-       EmpathyTpChatPriv     *priv;
-       EmpathyTpChat         *chat;
+       GObject           *chat;
+       EmpathyTpChatPriv *priv;
 
-       g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
-       g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL);
+       chat = G_OBJECT_CLASS (empathy_tp_chat_parent_class)->constructor (type, n_props, props);
 
-       chat = g_object_new (EMPATHY_TYPE_TP_CHAT, NULL);
        priv = GET_PRIV (chat);
-
+g_print ("**********tp_chat_constructor");
        priv->manager = empathy_contact_manager_new ();
-       priv->list = empathy_contact_manager_get_list (priv->manager, account);
-       priv->tp_chan = g_object_ref (tp_chan);
-       priv->account = g_object_ref (account);
+       priv->list = empathy_contact_manager_get_list (priv->manager, priv->account);
        priv->mc = gossip_mission_control_new ();
        g_object_ref (priv->list);
 
-       priv->text_iface = tp_chan_get_interface (tp_chan,
+       priv->text_iface = tp_chan_get_interface (priv->tp_chan,
                                                  TELEPATHY_CHAN_IFACE_TEXT_QUARK);
-       priv->chat_state_iface = tp_chan_get_interface (tp_chan,
+       priv->chat_state_iface = tp_chan_get_interface (priv->tp_chan,
                                                        TELEPATHY_CHAN_IFACE_CHAT_STATE_QUARK);
 
        g_signal_connect (priv->tp_chan, "destroy",
@@ -230,6 +266,62 @@ empathy_tp_chat_new (McAccount *account,
        return chat;
 }
 
+static void
+tp_chat_get_property (GObject    *object,
+                     guint       param_id,
+                     GValue     *value,
+                     GParamSpec *pspec)
+{
+       EmpathyTpChatPriv *priv;
+
+       priv = GET_PRIV (object);
+
+       switch (param_id) {
+       case PROP_ACCOUNT:
+               g_value_set_object (value, priv->account);
+               break;
+       case PROP_TP_CHAN:
+               g_value_set_object (value, priv->tp_chan);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+               break;
+       };
+}
+
+static void
+tp_chat_set_property (GObject      *object,
+                     guint         param_id,
+                     const GValue *value,
+                     GParamSpec   *pspec)
+{
+       EmpathyTpChatPriv *priv;
+
+       priv = GET_PRIV (object);
+
+       switch (param_id) {
+       case PROP_ACCOUNT:
+               priv->account = g_object_ref (g_value_get_object (value));
+               break;
+       case PROP_TP_CHAN:
+               priv->tp_chan = g_object_ref (g_value_get_object (value));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+               break;
+       };
+}
+
+EmpathyTpChat *
+empathy_tp_chat_new (McAccount *account,
+                    TpChan    *tp_chan)
+{
+       return g_object_new (EMPATHY_TYPE_TP_CHAT, 
+                            "account", account,
+                            "tp-chan", tp_chan,
+                            NULL);
+}
+
 EmpathyTpChat *
 empathy_tp_chat_new_with_contact (GossipContact *contact)
 {
@@ -538,7 +630,7 @@ tp_chat_state_changed_cb (DBusGProxy                *chat_state_iface,
 
        priv = GET_PRIV (chat);
 
-       contact = empathy_contact_list_get_from_handle (priv->list, handle);
+       contact = empathy_tp_contact_list_get_from_handle (priv->list, handle);
 
        gossip_debug (DEBUG_DOMAIN, "Chat state changed for %s (%d): %d",
                      gossip_contact_get_name (contact),
@@ -564,11 +656,11 @@ tp_chat_emit_message (EmpathyTpChat *chat,
        priv = GET_PRIV (chat);
 
        if (from_handle == 0) {
-               sender = empathy_contact_list_get_own (priv->list);
+               sender = empathy_tp_contact_list_get_user (priv->list);
                g_object_ref (sender);
        } else {
-               sender = empathy_contact_list_get_from_handle (priv->list,
-                                                              from_handle);
+               sender = empathy_tp_contact_list_get_from_handle (priv->list,
+                                                                 from_handle);
        }
 
        message = gossip_message_new (message_body);
diff --git a/libempathy/empathy-tp-chatroom.c b/libempathy/empathy-tp-chatroom.c
new file mode 100644 (file)
index 0000000..6feef54
--- /dev/null
@@ -0,0 +1,167 @@
+/* -*- 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 <libmissioncontrol/mission-control.h>
+
+#include "empathy-tp-chatroom.h"
+#include "empathy-tp-contact-list.h"
+#include "empathy-contact-list.h"
+#include "empathy-contact-manager.h"
+#include "gossip-telepathy-group.h"
+#include "gossip-utils.h"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+                      EMPATHY_TYPE_TP_CHATROOM, EmpathyTpChatroomPriv))
+
+#define DEBUG_DOMAIN "TpChatroom"
+
+struct _EmpathyTpChatroomPriv {
+       EmpathyContactManager *manager;
+       EmpathyTpContactList  *list;
+       GossipTelepathyGroup  *group;
+};
+
+static void            empathy_tp_chatroom_class_init (EmpathyTpChatroomClass  *klass);
+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_setup              (EmpathyContactList      *list);
+static GossipContact * tp_chatroom_find               (EmpathyContactList      *list,
+                                                      const gchar             *id);
+static void            tp_chatroom_add                (EmpathyContactList      *list,
+                                                      GossipContact           *contact,
+                                                      const gchar             *message);
+static void            tp_chatroom_remove             (EmpathyContactList      *list,
+                                                      GossipContact           *contact,
+                                                      const gchar             *message);
+static GList *         tp_chatroom_get_contacts       (EmpathyContactList      *list);
+
+G_DEFINE_TYPE_WITH_CODE (EmpathyTpChatroom, empathy_tp_chatroom, EMPATHY_TYPE_TP_CHAT,
+                        G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
+                                               tp_chatroom_iface_init));
+
+static void
+empathy_tp_chatroom_class_init (EmpathyTpChatroomClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->finalize = tp_chatroom_finalize;
+
+       g_type_class_add_private (object_class, sizeof (EmpathyTpChatroomPriv));
+}
+
+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_contacts = tp_chatroom_get_contacts;
+}
+
+static void
+empathy_tp_chatroom_init (EmpathyTpChatroom *chatroom)
+{
+}
+
+static void
+tp_chatroom_finalize (GObject *object)
+{
+       EmpathyTpChatroomPriv *priv;
+       EmpathyTpChatroom     *chatroom;
+
+       chatroom = EMPATHY_TP_CHATROOM (object);
+       priv = GET_PRIV (chatroom);
+
+       g_object_unref (priv->group);
+       g_object_unref (priv->manager);
+       g_object_unref (priv->list);
+
+       G_OBJECT_CLASS (empathy_tp_chatroom_parent_class)->finalize (object);
+}
+
+EmpathyTpChatroom *
+empathy_tp_chatroom_new (McAccount *account,
+                        TpChan    *tp_chan)
+{
+       EmpathyTpChatroomPriv *priv;
+       EmpathyTpChatroom     *chatroom;
+       TpConn                *tp_conn;
+       MissionControl        *mc;
+
+       g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
+       g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL);
+
+       chatroom = g_object_new (EMPATHY_TYPE_TP_CHATROOM,
+                                "account", account,
+                                "tp-chan", tp_chan,
+                                NULL);
+
+       priv = GET_PRIV (chatroom);
+
+       mc = gossip_mission_control_new ();
+       tp_conn = mission_control_get_connection (mc, account, NULL);
+       priv->manager = empathy_contact_manager_new ();
+       priv->group = gossip_telepathy_group_new (tp_chan, tp_conn);
+       priv->list = empathy_contact_manager_get_list (priv->manager, account);
+
+       g_object_unref (mc);
+       g_object_unref (tp_conn);
+
+       return chatroom;
+}
+
+static void
+tp_chatroom_setup (EmpathyContactList *list)
+{
+}
+
+static GossipContact *
+tp_chatroom_find (EmpathyContactList *list,
+                 const gchar        *id)
+{
+       return NULL;
+}
+
+static void
+tp_chatroom_add (EmpathyContactList *list,
+                GossipContact      *contact,
+                const gchar        *message)
+{
+}
+
+static void
+tp_chatroom_remove (EmpathyContactList *list,
+                   GossipContact      *contact,
+                   const gchar        *message)
+{
+}
+
+static GList *
+tp_chatroom_get_contacts (EmpathyContactList *list)
+{
+       return NULL;
+}
+
diff --git a/libempathy/empathy-tp-chatroom.h b/libempathy/empathy-tp-chatroom.h
new file mode 100644 (file)
index 0000000..75ccc58
--- /dev/null
@@ -0,0 +1,61 @@
+/* -*- 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_TP_CHATROOM_H__
+#define __EMPATHY_TP_CHATROOM_H__
+
+#include <glib.h>
+
+#include <libtelepathy/tp-chan.h>
+
+#include <libmissioncontrol/mc-account.h>
+
+#include "empathy-tp-chat.h"
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_TP_CHATROOM         (empathy_tp_chatroom_get_type ())
+#define EMPATHY_TP_CHATROOM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_TP_CHATROOM, EmpathyTpChatroom))
+#define EMPATHY_TP_CHATROOM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_TP_CHATROOM, EmpathyTpChatroomClass))
+#define EMPATHY_IS_TP_CHATROOM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_TP_CHATROOM))
+#define EMPATHY_IS_TP_CHATROOM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_TP_CHATROOM))
+#define EMPATHY_TP_CHATROOM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_TP_CHATROOM, EmpathyTpChatroomClass))
+
+typedef struct _EmpathyTpChatroom      EmpathyTpChatroom;
+typedef struct _EmpathyTpChatroomClass EmpathyTpChatroomClass;
+typedef struct _EmpathyTpChatroomPriv  EmpathyTpChatroomPriv;
+
+struct _EmpathyTpChatroom {
+       EmpathyTpChat parent;
+};
+
+struct _EmpathyTpChatroomClass {
+       EmpathyTpChatClass parent_class;
+};
+
+GType              empathy_tp_chatroom_get_type (void) G_GNUC_CONST;
+EmpathyTpChatroom *empathy_tp_chatroom_new      (McAccount *account,
+                                                TpChan    *tp_chan);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_TP_CHATROOM_H__ */
diff --git a/libempathy/empathy-tp-contact-list.c b/libempathy/empathy-tp-contact-list.c
new file mode 100644 (file)
index 0000000..43c317e
--- /dev/null
@@ -0,0 +1,1822 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 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
+ * 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-helpers.h>
+#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"
+#include "gossip-telepathy-group.h"
+#include "gossip-debug.h"
+#include "gossip-utils.h"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+                      EMPATHY_TYPE_TP_CONTACT_LIST, EmpathyTpContactListPriv))
+
+#define DEBUG_DOMAIN "TpContactList"
+#define MAX_AVATAR_REQUESTS 10
+
+struct _EmpathyTpContactListPriv {
+       TpConn               *tp_conn;
+       McAccount            *account;
+       MissionControl       *mc;
+       GossipContact        *user_contact;
+
+       GossipTelepathyGroup *known;
+       GossipTelepathyGroup *publish;
+       GossipTelepathyGroup *subscribe;
+
+       GHashTable           *groups;
+       GHashTable           *contacts;
+
+       DBusGProxy           *aliasing_iface;
+       DBusGProxy           *avatars_iface;
+       DBusGProxy           *presence_iface;
+
+       GList                *avatar_requests_queue;
+};
+
+typedef enum {
+       TP_CONTACT_LIST_TYPE_KNOWN,
+       TP_CONTACT_LIST_TYPE_PUBLISH,
+       TP_CONTACT_LIST_TYPE_SUBSCRIBE,
+       TP_CONTACT_LIST_TYPE_UNKNOWN,
+       TP_CONTACT_LIST_TYPE_COUNT
+} 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 GossipContact *        tp_contact_list_find                     (EmpathyContactList              *list,
+                                                                       const gchar                     *id);
+static void                   tp_contact_list_add                      (EmpathyContactList              *list,
+                                                                       GossipContact                   *contact,
+                                                                       const gchar                     *message);
+static void                   tp_contact_list_remove                   (EmpathyContactList              *list,
+                                                                       GossipContact                   *contact,
+                                                                       const gchar                     *message);
+static GList *                tp_contact_list_get_contacts             (EmpathyContactList              *list);
+static void                   tp_contact_list_contact_removed_foreach  (guint                            handle,
+                                                                       GossipContact                   *contact,
+                                                                       EmpathyTpContactList            *list);
+static void                   tp_contact_list_destroy_cb               (DBusGProxy                      *proxy,
+                                                                       EmpathyTpContactList            *list);
+static gboolean               tp_contact_list_find_foreach             (guint                            handle,
+                                                                       GossipContact                   *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,
+                                                                       TpChan                          *list_chan);
+static void                   tp_contact_list_contact_added_cb         (GossipTelepathyGroup            *group,
+                                                                       GArray                          *handles,
+                                                                       guint                            actor_handle,
+                                                                       guint                            reason,
+                                                                       const gchar                     *message,
+                                                                       EmpathyTpContactList            *list);
+static void                   tp_contact_list_contact_removed_cb       (GossipTelepathyGroup            *group,
+                                                                       GArray                          *handles,
+                                                                       guint                            actor_handle,
+                                                                       guint                            reason,
+                                                                       const gchar                     *message,
+                                                                       EmpathyTpContactList            *list);
+static void                   tp_contact_list_local_pending_cb         (GossipTelepathyGroup            *group,
+                                                                       GArray                          *handles,
+                                                                       guint                            actor_handle,
+                                                                       guint                            reason,
+                                                                       const gchar                     *message,
+                                                                       EmpathyTpContactList            *list);
+static void                   tp_contact_list_groups_updated_cb        (GossipContact                   *contact,
+                                                                       GParamSpec                      *param,
+                                                                       EmpathyTpContactList            *list);
+static void                   tp_contact_list_subscription_updated_cb  (GossipContact                   *contact,
+                                                                       GParamSpec                      *param,
+                                                                       EmpathyTpContactList            *list);
+static void                   tp_contact_list_name_updated_cb          (GossipContact                   *contact,
+                                                                       GParamSpec                      *param,
+                                                                       EmpathyTpContactList            *list);
+static void                   tp_contact_list_update_groups_foreach    (gchar                           *object_path,
+                                                                       GossipTelepathyGroup            *group,
+                                                                       TpContactListData               *data);
+static GossipTelepathyGroup * tp_contact_list_get_group                (EmpathyTpContactList            *list,
+                                                                       const gchar                     *name);
+static gboolean               tp_contact_list_find_group               (gchar                           *key,
+                                                                       GossipTelepathyGroup            *group,
+                                                                       gchar                           *group_name);
+static void                   tp_contact_list_get_groups_foreach       (gchar                           *key,
+                                                                       GossipTelepathyGroup            *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   (GossipTelepathyGroup            *group,
+                                                                       GArray                          *members,
+                                                                       guint                            actor_handle,
+                                                                       guint                            reason,
+                                                                       const gchar                     *message,
+                                                                       EmpathyTpContactList            *list);
+static void                   tp_contact_list_group_members_removed_cb (GossipTelepathyGroup            *group,
+                                                                       GArray                          *members,
+                                                                       guint                            actor_handle,
+                                                                       guint                            reason,
+                                                                       const gchar                     *message,
+                                                                       EmpathyTpContactList            *list);
+static void                   tp_contact_list_get_contacts_foreach     (guint                            handle,
+                                                                       GossipContact                   *contact,
+                                                                       GList                          **contacts);
+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    (EmpathyTpContactList            *list);
+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,
+                                                                       GossipPresence                 **presence);
+static void                   tp_contact_list_status_changed_cb        (MissionControl                  *mc,
+                                                                       TelepathyConnectionStatus        status,
+                                                                       McPresence                       presence,
+                                                                       TelepathyConnectionStatusReason  reason,
+                                                                       const gchar                     *unique_name,
+                                                                       EmpathyTpContactList            *list);
+
+enum {
+       DESTROY,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+static guint n_avatar_requests = 0;
+
+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_contacts = tp_contact_list_get_contacts;
+}
+
+static void
+empathy_tp_contact_list_init (EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+
+       priv = GET_PRIV (list);
+
+       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);
+}
+
+static void
+tp_contact_list_finalize (GObject *object)
+{
+       EmpathyTpContactListPriv *priv;
+       EmpathyTpContactList     *list;
+
+       list = EMPATHY_TP_CONTACT_LIST (object);
+       priv = GET_PRIV (list);
+
+       gossip_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);
+
+       tp_contact_list_finalize_proxies (list);
+
+       if (priv->tp_conn) {
+               g_object_unref (priv->tp_conn);
+       }
+
+       if (priv->known) {
+               g_object_unref (priv->known);
+       }
+
+       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_OBJECT_CLASS (empathy_tp_contact_list_parent_class)->finalize (object);
+}
+
+EmpathyTpContactList *
+empathy_tp_contact_list_new (McAccount *account)
+{
+       EmpathyTpContactListPriv *priv;
+       EmpathyTpContactList     *list;
+       MissionControl           *mc;
+       guint                     handle;
+       GError                   *error = NULL;
+
+       g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
+
+       mc = gossip_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;
+
+       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 (priv->presence_iface) {
+               dbus_g_proxy_connect_signal (priv->presence_iface,
+                                            "PresenceUpdate",
+                                            G_CALLBACK (tp_contact_list_presence_update_cb),
+                                            list, NULL);
+       }
+
+       /* Get our own handle and contact */
+       if (!tp_conn_get_self_handle (DBUS_G_PROXY (priv->tp_conn),
+                                     &handle, &error)) {
+               gossip_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);
+       }
+
+       return list;
+}
+
+static void
+tp_contact_list_setup (EmpathyContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+       GPtrArray                *channels;
+       GError                   *error = NULL;
+       guint                     i;
+
+       g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list));
+
+       priv = GET_PRIV (list);
+
+       gossip_debug (DEBUG_DOMAIN, "setup contact list: %p", list);
+
+       dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel",
+                                    G_CALLBACK (tp_contact_list_newchannel_cb),
+                                    list, NULL);
+
+       /* Get existing channels */
+       if (!tp_conn_list_channels (DBUS_G_PROXY (priv->tp_conn),
+                                   &channels,
+                                   &error)) {
+               gossip_debug (DEBUG_DOMAIN,
+                             "Failed to get list of open channels: %s",
+                             error ? error->message : "No error given");
+               g_clear_error (&error);
+               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);
+       }
+
+       g_ptr_array_free (channels, TRUE);
+}
+
+static GossipContact *
+tp_contact_list_find (EmpathyContactList *list,
+                     const gchar        *id)
+{
+       EmpathyTpContactListPriv *priv;
+       GossipContact            *contact;
+
+       g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL);
+
+       priv = GET_PRIV (list);
+
+       contact = g_hash_table_find (priv->contacts,
+                                    (GHRFunc) tp_contact_list_find_foreach,
+                                    (gchar*) id);
+
+       return NULL;
+}
+
+static void
+tp_contact_list_add (EmpathyContactList *list,
+                    GossipContact      *contact,
+                    const gchar        *message)
+{
+       EmpathyTpContactListPriv *priv;
+       guint                     handle;
+
+       g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list));
+
+       priv = GET_PRIV (list);
+
+       handle = gossip_contact_get_handle (contact);
+       gossip_telepathy_group_add_member (priv->subscribe, handle, message);
+}
+
+static void
+tp_contact_list_remove (EmpathyContactList *list,
+                       GossipContact      *contact,
+                       const gchar        *message)
+{
+       EmpathyTpContactListPriv *priv;
+       guint                     handle;
+
+       g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list));
+
+       priv = GET_PRIV (list);
+
+       handle = gossip_contact_get_handle (contact);
+       gossip_telepathy_group_remove_member (priv->subscribe, handle, message);
+       gossip_telepathy_group_remove_member (priv->publish, handle, message);
+       gossip_telepathy_group_remove_member (priv->known, handle, message);
+}
+
+static GList *
+tp_contact_list_get_contacts (EmpathyContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+       GList                    *contacts = NULL;
+
+       g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL);
+
+       priv = GET_PRIV (list);
+
+       /* FIXME: we should only return contacts that are in the contact list */
+       g_hash_table_foreach (priv->contacts,
+                             (GHFunc) tp_contact_list_get_contacts_foreach,
+                             &contacts);
+
+       return contacts;
+}
+
+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;
+}
+
+GossipContact *
+empathy_tp_contact_list_get_user (EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+
+       g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL);
+
+       priv = GET_PRIV (list);
+       
+       return priv->user_contact;
+}
+
+GossipContact *
+empathy_tp_contact_list_get_from_id (EmpathyTpContactList *list,
+                                    const gchar          *id)
+{
+       EmpathyTpContactListPriv *priv;
+       GossipContact            *contact;
+       const gchar              *contact_ids[] = {id, NULL};
+       GArray                   *handles;
+       guint                     handle;
+       GError                   *error = NULL;
+
+       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 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)) {
+               gossip_debug (DEBUG_DOMAIN, 
+                             "RequestHandle for %s failed: %s", id,
+                             error ? error->message : "No error given");
+               g_clear_error (&error);
+               return 0;
+       }
+
+       handle = g_array_index(handles, guint, 0);
+       g_array_free (handles, TRUE);
+
+       return empathy_tp_contact_list_get_from_handle (list, handle);
+}
+
+GossipContact *
+empathy_tp_contact_list_get_from_handle (EmpathyTpContactList *list,
+                                        guint                 handle)
+{
+       GossipContact *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;
+       }
+
+       contact = contacts->data;
+       g_list_free (contacts);
+
+       return contact;
+}
+
+GList *
+empathy_tp_contact_list_get_from_handles (EmpathyTpContactList *list,
+                                         GArray               *handles)
+{
+       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++) {
+               GossipContact *contact;
+               guint          handle;
+
+               handle = g_array_index (handles, guint, i);
+               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);
+               }
+       }
+
+       if (new_handles->len == 0) {
+               return contacts;
+       }
+
+       /* 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)) {
+               gossip_debug (DEBUG_DOMAIN, 
+                             "HoldHandles Error: %s",
+                             error ? error->message : "No error given");
+               g_clear_error (&error);
+               g_array_free (new_handles, TRUE);
+               return contacts;
+       }
+
+       /* 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)) {
+               gossip_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++) {
+               GossipContact *contact;
+               guint          handle;
+
+               handle = g_array_index (new_handles, guint, i);
+               contact = g_object_new (GOSSIP_TYPE_CONTACT,
+                                       "account", priv->account,
+                                       "id", *id,
+                                       "handle", handle,
+                                       NULL);
+
+               g_signal_connect (contact, "notify::groups",
+                                 G_CALLBACK (tp_contact_list_groups_updated_cb),
+                                 list);
+               g_signal_connect (contact, "notify::subscription",
+                                 G_CALLBACK (tp_contact_list_subscription_updated_cb),
+                                 list);
+               g_signal_connect (contact, "notify::name",
+                                 G_CALLBACK (tp_contact_list_name_updated_cb),
+                                 list);
+
+               gossip_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)
+{
+       EmpathyTpContactListPriv *priv;
+       GossipTelepathyGroup     *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;
+       }
+
+       gossip_debug (DEBUG_DOMAIN, "rename group %s to %s", group, new_group);
+
+       /* Remove all members from the old group */
+       members = gossip_telepathy_group_get_members (group);
+       gossip_telepathy_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,
+                            gossip_telepathy_group_get_object_path (group));
+
+       /* Add all members to the new group */
+       group = tp_contact_list_get_group (list, new_group);
+       if (group) {
+               gossip_telepathy_group_add_members (group, members, "");
+       }
+}
+
+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)
+{
+       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);
+       }
+
+       if (priv->avatars_iface) {
+               dbus_g_proxy_disconnect_signal (priv->avatars_iface,
+                                               "AvatarUpdated",
+                                               G_CALLBACK (tp_contact_list_avatar_update_cb),
+                                               list);
+       }
+
+       if (priv->presence_iface) {
+               dbus_g_proxy_disconnect_signal (priv->presence_iface,
+                                               "PresenceUpdate",
+                                               G_CALLBACK (tp_contact_list_presence_update_cb),
+                                               list);
+       }
+}
+
+static void
+tp_contact_list_destroy_cb (DBusGProxy           *proxy,
+                           EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+
+       priv = GET_PRIV (list);
+
+       gossip_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,
+                                        GossipContact        *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_subscription_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,
+                              GossipContact        *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_subscription_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,
+                                GossipContact        *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_subscription_updated_cb,
+                                          list);
+       g_signal_handlers_unblock_by_func (contact,
+                                          tp_contact_list_name_updated_cb,
+                                          list);
+}
+
+static gboolean
+tp_contact_list_find_foreach (guint          handle,
+                             GossipContact *contact,
+                             gchar         *id)
+{
+       if (strcmp (gossip_contact_get_id (contact), id) == 0) {
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+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)
+{
+       EmpathyTpContactListPriv *priv;
+       GossipTelepathyGroup     *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) {
+               return;
+       }
+
+       bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (priv->tp_conn));
+       new_chan = tp_chan_new (tp_get_bus (),
+                               bus_name,
+                               object_path,
+                               channel_type, handle_type, channel_handle);
+
+       if (handle_type == TP_HANDLE_TYPE_LIST) {
+               TpContactListType list_type;
+
+               list_type = tp_contact_list_get_type (list, new_chan);
+               if (list_type == TP_CONTACT_LIST_TYPE_UNKNOWN) {
+                       gossip_debug (DEBUG_DOMAIN, "Unknown contact list channel");
+                       g_object_unref (new_chan);
+                       return;
+               }
+
+               gossip_debug (DEBUG_DOMAIN, "New contact list channel of type: %d",
+                             list_type);
+
+               group = gossip_telepathy_group_new (new_chan, priv->tp_conn);
+
+               switch (list_type) {
+               case TP_CONTACT_LIST_TYPE_KNOWN:
+                       if (priv->known) {
+                               g_object_unref (priv->known);
+                       }
+                       priv->known = group;
+                       break;
+               case TP_CONTACT_LIST_TYPE_PUBLISH:
+                       if (priv->publish) {
+                               g_object_unref (priv->publish);
+                       }
+                       priv->publish = group;
+                       break;
+               case TP_CONTACT_LIST_TYPE_SUBSCRIBE:
+                       if (priv->subscribe) {
+                               g_object_unref (priv->subscribe);
+                       }
+                       priv->subscribe = group;
+                       break;
+               default:
+                       g_assert_not_reached ();
+               }
+
+               /* Connect and setup the new contact-list group */
+               if (list_type == TP_CONTACT_LIST_TYPE_KNOWN ||
+                   list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) {
+                       g_signal_connect (group, "members-added",
+                                         G_CALLBACK (tp_contact_list_contact_added_cb),
+                                         list);
+                       g_signal_connect (group, "members-removed",
+                                         G_CALLBACK (tp_contact_list_contact_removed_cb),
+                                         list);
+
+                       members = gossip_telepathy_group_get_members (group);
+                       tp_contact_list_contact_added_cb (group, members, 0,
+                                                      TP_CHANNEL_GROUP_CHANGE_REASON_NONE,
+                                                      NULL, list);
+                       g_array_free (members, TRUE);
+               }
+               if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) {
+                       GPtrArray *info;
+                       GArray    *pending; 
+                       guint      i;
+
+                       g_signal_connect (group, "local-pending",
+                                         G_CALLBACK (tp_contact_list_local_pending_cb),
+                                         list);
+
+                       info = gossip_telepathy_group_get_local_pending_members_with_info (group);
+
+                       if (!info) {
+                               /* This happens with butterfly because
+                                * GetLocalPendingMembersWithInfo is not 
+                                * implemented */
+                               g_object_unref (new_chan);
+                               return;
+                       }
+
+                       pending = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
+                       for (i = 0; info->len > i; i++) {
+                               GValueArray *pending_struct;
+                               guint        member;
+                               guint        invitor;
+                               guint        reason;
+                               const gchar *message;
+
+                               pending_struct = g_ptr_array_index (info, i);
+                               member = g_value_get_uint (g_value_array_get_nth (pending_struct, 0));
+                               invitor = 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 (pending, 0, member);
+
+                               tp_contact_list_local_pending_cb (group, pending,
+                                                              invitor,
+                                                              reason,
+                                                              message, list);
+
+                               g_value_array_free (pending_struct);
+                       }
+
+                       g_ptr_array_free (info, TRUE);
+                       g_array_free (pending, TRUE);
+               }
+       }
+       else if (handle_type == TP_HANDLE_TYPE_GROUP) {
+               const gchar *object_path;
+
+               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);
+                       return;
+               }
+
+               group = gossip_telepathy_group_new (new_chan, priv->tp_conn);
+
+               gossip_debug (DEBUG_DOMAIN, "New server-side group channel: %s",
+                             gossip_telepathy_group_get_name (group));
+
+               dbus_g_proxy_connect_signal (DBUS_G_PROXY (new_chan), "Closed",
+                                            G_CALLBACK
+                                            (tp_contact_list_group_channel_closed_cb),
+                                            list, NULL);
+
+               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),
+                                 list);
+               g_signal_connect (group, "members-removed",
+                                 G_CALLBACK (tp_contact_list_group_members_removed_cb),
+                                 list);
+
+               members = gossip_telepathy_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);
+       }
+
+       g_object_unref (new_chan);
+}
+
+static TpContactListType
+tp_contact_list_get_type (EmpathyTpContactList *list,
+                         TpChan               *list_chan)
+{
+       EmpathyTpContactListPriv  *priv;
+       GArray                    *handles;
+       gchar                    **handle_name;
+       TpContactListType          list_type;
+       GError                    *error = NULL;
+
+       priv = GET_PRIV (list);
+
+       handles = g_array_new (FALSE, FALSE, sizeof (guint));
+       g_array_append_val (handles, list_chan->handle);
+
+       if (!tp_conn_inspect_handles (DBUS_G_PROXY (priv->tp_conn),
+                                     TP_HANDLE_TYPE_LIST,
+                                     handles,
+                                     &handle_name,
+                                     &error)) {
+               gossip_debug (DEBUG_DOMAIN, 
+                             "InspectHandle Error: %s",
+                             error ? error->message : "No error given");
+               g_clear_error (&error);
+               g_array_free (handles, TRUE);
+               return TP_CONTACT_LIST_TYPE_UNKNOWN;
+       }
+
+       if (strcmp (*handle_name, "subscribe") == 0) {
+               list_type = TP_CONTACT_LIST_TYPE_SUBSCRIBE;
+       } else if (strcmp (*handle_name, "publish") == 0) {
+               list_type = TP_CONTACT_LIST_TYPE_PUBLISH;
+       } else if (strcmp (*handle_name, "known") == 0) {
+               list_type = TP_CONTACT_LIST_TYPE_KNOWN;
+       } else {
+               list_type = TP_CONTACT_LIST_TYPE_UNKNOWN;
+       }
+
+       g_strfreev (handle_name);
+       g_array_free (handles, TRUE);
+
+       return list_type;
+}
+
+static void
+tp_contact_list_contact_added_cb (GossipTelepathyGroup *group,
+                                 GArray               *handles,
+                                 guint                 actor_handle,
+                                 guint                 reason,
+                                 const gchar          *message,
+                                 EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+       GList                    *added_list, *l;
+
+       priv = GET_PRIV (list);
+
+       added_list = empathy_tp_contact_list_get_from_handles (list, handles);
+
+       for (l = added_list; l; l = l->next) {
+               GossipContact *contact;
+
+               contact = GOSSIP_CONTACT (l->data);
+               tp_contact_list_block_contact (list, contact);
+               gossip_contact_set_subscription (contact, GOSSIP_SUBSCRIPTION_BOTH);
+               tp_contact_list_unblock_contact (list, contact);
+
+               g_signal_emit_by_name (list, "contact-added", contact);
+
+               g_object_unref (contact);
+       }
+
+       g_list_free (added_list);
+}
+
+static void
+tp_contact_list_contact_removed_cb (GossipTelepathyGroup *group,
+                                   GArray               *handles,
+                                   guint                 actor_handle,
+                                   guint                 reason,
+                                   const gchar          *message,
+                                   EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+       GList                    *removed_list, *l;
+
+       priv = GET_PRIV (list);
+
+       removed_list = empathy_tp_contact_list_get_from_handles (list, handles);
+
+       for (l = removed_list; l; l = l->next) {
+               GossipContact *contact;
+               guint          handle;
+
+               contact = GOSSIP_CONTACT (l->data);
+
+               handle = gossip_contact_get_handle (contact);
+               g_hash_table_remove (priv->contacts, GUINT_TO_POINTER (handle));
+
+               g_signal_emit_by_name (list, "contact-removed", contact);
+
+               g_object_unref (contact);
+       }
+
+       g_list_free (removed_list);
+}
+
+static void
+tp_contact_list_local_pending_cb (GossipTelepathyGroup *group,
+                                 GArray               *handles,
+                                 guint                 actor_handle,
+                                 guint                 reason,
+                                 const gchar          *message,
+                                 EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+       GList                    *pending_list, *l;
+
+       priv = GET_PRIV (list);
+
+       pending_list = empathy_tp_contact_list_get_from_handles (list, handles);
+
+       for (l = pending_list; l; l = l->next) {
+               GossipContact *contact;
+
+               contact = GOSSIP_CONTACT (l->data);
+
+               /* FIXME: Is that the correct way ? */
+               tp_contact_list_block_contact (list, contact);
+               gossip_contact_set_subscription (contact, GOSSIP_SUBSCRIPTION_FROM);
+               tp_contact_list_unblock_contact (list, contact);
+               g_signal_emit_by_name (list, "contact-added", contact);
+
+               g_object_unref (contact);
+       }
+
+       g_list_free (pending_list);
+}
+
+static void
+tp_contact_list_groups_updated_cb (GossipContact        *contact,
+                                  GParamSpec           *param,
+                                  EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+       TpContactListData         data;
+       GList                    *groups, *l;
+
+       priv = GET_PRIV (list);
+
+       /* Make sure all groups are created */
+       groups = gossip_contact_get_groups (contact);
+       for (l = groups; l; l = l->next) {
+               tp_contact_list_get_group (list, l->data);
+       }
+
+       data.handle = gossip_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_subscription_updated_cb (GossipContact        *contact,
+                                        GParamSpec           *param,
+                                        EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+       GossipSubscription        subscription;
+       guint                     handle;
+
+       priv = GET_PRIV (list);
+
+       subscription = gossip_contact_get_subscription (contact);
+       handle = gossip_contact_get_handle (contact);
+
+       /* FIXME: what to do here, I'm a bit lost... */
+       if (subscription) {
+               gossip_telepathy_group_add_member (priv->publish, handle, "");
+       } else {
+               gossip_telepathy_group_remove_member (priv->publish, handle, "");
+       }
+}
+
+static void
+tp_contact_list_name_updated_cb (GossipContact        *contact,
+                                GParamSpec           *param,
+                                EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+       GHashTable               *new_alias;
+       const gchar              *new_name;
+       guint                     handle;
+       GError                   *error = NULL;
+
+       priv = GET_PRIV (list);
+       
+       handle = gossip_contact_get_handle (contact);
+       new_name = gossip_contact_get_name (contact);
+
+       gossip_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));
+
+       if (!tp_conn_iface_aliasing_set_aliases (priv->aliasing_iface,
+                                                new_alias,
+                                                &error)) {
+               gossip_debug (DEBUG_DOMAIN, 
+                             "Couldn't rename contact: %s",
+                             error ? error->message : "No error given");
+               g_clear_error (&error);
+       }
+
+       g_hash_table_destroy (new_alias);
+}
+
+static void
+tp_contact_list_update_groups_foreach (gchar                *object_path,
+                                      GossipTelepathyGroup *group,
+                                      TpContactListData    *data)
+{
+       gboolean     is_member;
+       gboolean     found = FALSE;
+       const gchar *group_name;
+       GList       *l;
+
+       is_member = gossip_telepathy_group_is_member (group, data->handle);
+       group_name = gossip_telepathy_group_get_name (group);
+
+       for (l = data->new_groups; l; l = l->next) {
+               if (strcmp (group_name, l->data) == 0) {
+                       found = TRUE;
+                       break;
+               }
+       }
+
+       if (is_member && !found) {
+               /* We are no longer member of this group */
+               gossip_debug (DEBUG_DOMAIN, "Contact %d removed from group '%s'",
+                             data->handle, group_name);
+               gossip_telepathy_group_remove_member (group, data->handle, "");
+       }
+
+       if (!is_member && found) {
+               /* We are now member of this group */
+               gossip_debug (DEBUG_DOMAIN, "Contact %d added to group '%s'",
+                             data->handle, group_name);
+               gossip_telepathy_group_add_member (group, data->handle, "");
+       }
+}
+
+static GossipTelepathyGroup *
+tp_contact_list_get_group (EmpathyTpContactList *list,
+                          const gchar          *name)
+{
+       EmpathyTpContactListPriv *priv;
+       GossipTelepathyGroup     *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);
+
+       group = g_hash_table_find (priv->groups,
+                                  (GHRFunc) tp_contact_list_find_group,
+                                  (gchar*) name);
+       if (group) {
+               return group;
+       }
+
+       gossip_debug (DEBUG_DOMAIN, "creating new group: %s", name);
+
+       if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn),
+                                     TP_HANDLE_TYPE_GROUP,
+                                     names,
+                                     &handles,
+                                     &error)) {
+               gossip_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;
+       }
+       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)) {
+               gossip_debug (DEBUG_DOMAIN,
+                             "Couldn't request the creation of a new group channel: %s",
+                             error ? error->message : "No error given");
+               g_clear_error (&error);
+               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 = gossip_telepathy_group_new (group_channel, priv->tp_conn);
+       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);
+
+       return group;
+}
+
+static gboolean
+tp_contact_list_find_group (gchar                 *key,
+                           GossipTelepathyGroup  *group,
+                           gchar                 *group_name)
+{
+       if (strcmp (group_name, gossip_telepathy_group_get_name (group)) == 0) {
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+tp_contact_list_get_groups_foreach (gchar                 *key,
+                                   GossipTelepathyGroup  *group,
+                                   GList                **groups)
+{
+       const gchar *name;
+
+       name = gossip_telepathy_group_get_name (group);
+       *groups = g_list_append (*groups, g_strdup (name));
+}
+
+static void
+tp_contact_list_group_channel_closed_cb (TpChan             *channel,
+                                        EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+
+       priv = GET_PRIV (list);
+
+       g_hash_table_remove (priv->groups,
+                            dbus_g_proxy_get_path (DBUS_G_PROXY (channel)));
+}
+
+static void
+tp_contact_list_group_members_added_cb (GossipTelepathyGroup *group,
+                                       GArray               *members,
+                                       guint                 actor_handle,
+                                       guint                 reason,
+                                       const gchar          *message,
+                                       EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+       GList                    *added_list, *l;
+       const gchar              *group_name;
+
+       priv = GET_PRIV (list);
+
+       group_name = gossip_telepathy_group_get_name (group);
+       added_list = empathy_tp_contact_list_get_from_handles (list, members);
+
+       for (l = added_list; l; l = l->next) {
+               GossipContact *contact;
+               GList         *contact_groups;
+
+               contact = GOSSIP_CONTACT (l->data);
+               contact_groups = gossip_contact_get_groups (contact);
+
+               if (!g_list_find_custom (contact_groups,
+                                        group_name,
+                                        (GCompareFunc) strcmp)) {
+                       gossip_debug (DEBUG_DOMAIN, "Contact %s added to group '%s'",
+                                     gossip_contact_get_name (contact),
+                                     group_name);
+                       contact_groups = g_list_append (contact_groups,
+                                                       g_strdup (group_name));
+                       tp_contact_list_block_contact (list, contact);
+                       gossip_contact_set_groups (contact, contact_groups);
+                       tp_contact_list_unblock_contact (list, contact);
+               }
+
+               g_object_unref (contact);
+       }
+
+       g_list_free (added_list);
+}
+
+static void
+tp_contact_list_group_members_removed_cb (GossipTelepathyGroup *group,
+                                         GArray               *members,
+                                         guint                 actor_handle,
+                                         guint                 reason,
+                                         const gchar          *message,
+                                         EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+       GList                    *removed_list, *l;
+       const gchar              *group_name;
+
+       priv = GET_PRIV (list);
+
+       group_name = gossip_telepathy_group_get_name (group);
+       removed_list = empathy_tp_contact_list_get_from_handles (list, members);
+
+       for (l = removed_list; l; l = l->next) {
+               GossipContact *contact;
+               GList         *contact_groups;
+               GList         *to_remove;
+
+               /* FIXME: Does it leak ? */
+               contact = GOSSIP_CONTACT (l->data);
+               contact_groups = gossip_contact_get_groups (contact);
+               contact_groups = g_list_copy (contact_groups);
+
+               to_remove = g_list_find_custom (contact_groups,
+                                               group_name,
+                                               (GCompareFunc) strcmp);
+               if (to_remove) {
+                       gossip_debug (DEBUG_DOMAIN, "Contact %d removed from group '%s'",
+                                     gossip_contact_get_handle (contact),
+                                     group_name);
+                       contact_groups = g_list_remove_link (contact_groups,
+                                                            to_remove);
+                       tp_contact_list_block_contact (list, contact);
+                       gossip_contact_set_groups (contact, contact_groups);
+                       tp_contact_list_unblock_contact (list, contact);
+               }
+
+               g_list_free (contact_groups);
+
+               g_object_unref (contact);
+       }
+
+       g_list_free (removed_list);
+}
+
+static void
+tp_contact_list_get_contacts_foreach (guint           handle,
+                                     GossipContact  *contact,
+                                     GList         **contacts)
+{
+       *contacts = g_list_append (*contacts, g_object_ref (contact));
+}
+
+static void
+tp_contact_list_get_info (EmpathyTpContactList *list,
+                         GArray               *handles)
+{
+       EmpathyTpContactListPriv *priv;
+       GError                   *error = NULL;
+
+       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)) {
+                       gossip_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;
+
+                       handle = g_array_index (handles, gint, i);
+                       tp_contact_list_request_avatar (list, handle);
+               }
+       }
+}
+
+static void
+tp_contact_list_request_avatar (EmpathyTpContactList *list,
+                               guint                 handle)
+{
+       EmpathyTpContactListPriv *priv;
+
+       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 */
+       priv->avatar_requests_queue = g_list_append (priv->avatar_requests_queue,
+                                                    GUINT_TO_POINTER (handle));
+       tp_contact_list_start_avatar_requests (list);
+}
+
+static void
+tp_contact_list_start_avatar_requests (EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv       *priv;
+       TpContactListAvatarRequestData *data;
+
+       priv = GET_PRIV (list);
+
+       while (n_avatar_requests <  MAX_AVATAR_REQUESTS &&
+              priv->avatar_requests_queue) {
+               data = g_slice_new (TpContactListAvatarRequestData);
+               data->list = list;
+               data->handle = GPOINTER_TO_UINT (priv->avatar_requests_queue->data);
+
+               n_avatar_requests++;
+               priv->avatar_requests_queue = g_list_remove (priv->avatar_requests_queue,
+                                                            priv->avatar_requests_queue->data);
+
+               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);
+       }
+}
+
+static void
+tp_contact_list_avatar_update_cb (DBusGProxy           *proxy,
+                                 guint                 handle,
+                                 gchar                *new_token,
+                                 EmpathyTpContactList *list)
+{
+       gossip_debug (DEBUG_DOMAIN, "Changing avatar for %d to %s",
+                     handle, new_token);
+
+       tp_contact_list_request_avatar (list, handle);
+}
+
+static void
+tp_contact_list_request_avatar_cb (DBusGProxy                     *proxy,
+                                  GArray                         *avatar_data,
+                                  gchar                          *mime_type,
+                                  GError                         *error,
+                                  TpContactListAvatarRequestData *data)
+{
+       GossipContact *contact;
+
+       contact = empathy_tp_contact_list_get_from_handle (data->list, data->handle);
+
+       if (error) {
+               gossip_debug (DEBUG_DOMAIN, "Error requesting avatar for %s: %s",
+                             gossip_contact_get_name (contact),
+                             error ? error->message : "No error given");
+       } else {
+               GossipAvatar *avatar;
+
+               avatar = gossip_avatar_new (avatar_data->data,
+                                           avatar_data->len,
+                                           mime_type);
+               tp_contact_list_block_contact (data->list, contact);
+               gossip_contact_set_avatar (contact, avatar);
+               tp_contact_list_unblock_contact (data->list, contact);
+               gossip_avatar_unref (avatar);
+       }
+
+       n_avatar_requests--;
+       tp_contact_list_start_avatar_requests (data->list);
+
+       g_slice_free (TpContactListAvatarRequestData, data);
+}
+
+static void
+tp_contact_list_aliases_update_cb (DBusGProxy           *proxy,
+                                  GPtrArray            *renamed_handlers,
+                                  EmpathyTpContactList *list)
+{
+       gint i;
+
+       for (i = 0; renamed_handlers->len > i; i++) {
+               guint          handle;
+               const gchar   *alias;
+               GValueArray   *renamed_struct;
+               GossipContact *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));
+
+               if (alias && *alias == '\0') {
+                       alias = NULL;
+               }
+
+               contact = empathy_tp_contact_list_get_from_handle (list, handle);
+               tp_contact_list_block_contact (list, contact);
+               gossip_contact_set_name (contact, alias);
+               tp_contact_list_unblock_contact (list, contact);
+
+               gossip_debug (DEBUG_DOMAIN, "contact %d renamed to %s (update cb)",
+                             handle, alias);
+       }
+}
+
+static void
+tp_contact_list_request_aliases_cb (DBusGProxy                       *proxy,
+                                   gchar                           **contact_names,
+                                   GError                           *error,
+                                   TpContactListAliasesRequestData  *data)
+{
+       guint   i = 0;
+       gchar **name;
+
+       for (name = contact_names; *name && !error; name++) {
+               GossipContact *contact;
+
+               contact = empathy_tp_contact_list_get_from_handle (data->list,
+                                                               data->handles[i]);
+               tp_contact_list_block_contact (data->list, contact);
+               gossip_contact_set_name (contact, *name);
+               tp_contact_list_unblock_contact (data->list, contact);
+
+               gossip_debug (DEBUG_DOMAIN, "contact %d renamed to %s (request cb)",
+                             data->handles[i], *name);
+
+               i++;
+       }
+
+       g_free (data->handles);
+       g_slice_free (TpContactListAliasesRequestData, data);
+}
+
+static void
+tp_contact_list_presence_update_cb (DBusGProxy           *proxy,
+                                   GHashTable           *handle_table,
+                                   EmpathyTpContactList *list)
+{
+       g_hash_table_foreach (handle_table,
+                             (GHFunc) tp_contact_list_parse_presence_foreach,
+                             list);
+}
+
+static void
+tp_contact_list_parse_presence_foreach (guint                 handle,
+                                       GValueArray          *presence_struct,
+                                       EmpathyTpContactList *list)
+{
+       GHashTable     *presences_table;
+       GossipContact  *contact;
+       GossipPresence *presence = NULL;
+
+       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);
+
+       gossip_debug (DEBUG_DOMAIN, "Presence changed for %s (%d) to %s (%d)",
+                     gossip_contact_get_name (contact),
+                     handle,
+                     presence ? gossip_presence_get_status (presence) : "unset",
+                     presence ? gossip_presence_get_state (presence) : MC_PRESENCE_UNSET);
+
+       tp_contact_list_block_contact (list, contact);
+       gossip_contact_set_presence (contact, presence);
+       tp_contact_list_unblock_contact (list, contact);
+}
+
+static void
+tp_contact_list_presences_table_foreach (const gchar     *state_str,
+                                        GHashTable      *presences_table,
+                                        GossipPresence **presence)
+{
+       McPresence    state;
+       const GValue *message;
+
+       state = gossip_presence_state_from_str (state_str);
+       if ((state == MC_PRESENCE_UNSET) || (state == MC_PRESENCE_OFFLINE)) {
+               return;
+       }
+
+       if (*presence) {
+               g_object_unref (*presence);
+               *presence = NULL;
+       }
+
+       *presence = gossip_presence_new ();
+       gossip_presence_set_state (*presence, state);
+
+       message = g_hash_table_lookup (presences_table, "message");
+       if (message != NULL) {
+               gossip_presence_set_status (*presence,
+                                           g_value_get_string (message));
+       }
+}
+
+static void
+tp_contact_list_status_changed_cb (MissionControl                  *mc,
+                                  TelepathyConnectionStatus        status,
+                                  McPresence                       presence,
+                                  TelepathyConnectionStatusReason  reason,
+                                  const gchar                     *unique_name,
+                                  EmpathyTpContactList            *list)
+{
+       EmpathyTpContactListPriv *priv;
+       McAccount                *account;
+
+       priv = GET_PRIV (list);
+
+       account = mc_account_lookup (unique_name);
+       if (status != TP_CONN_STATUS_DISCONNECTED ||
+           !gossip_account_equal (account, priv->account)) {
+               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);
+}
+
diff --git a/libempathy/empathy-tp-contact-list.h b/libempathy/empathy-tp-contact-list.h
new file mode 100644 (file)
index 0000000..5807413
--- /dev/null
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 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
+ * 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_TP_CONTACT_LIST_H__
+#define __EMPATHY_TP_CONTACT_LIST_H__
+
+#include <glib.h>
+#include <libmissioncontrol/mc-account.h>
+
+#include "gossip-contact.h"
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_TP_CONTACT_LIST         (empathy_tp_contact_list_get_type ())
+#define EMPATHY_TP_CONTACT_LIST(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CONTACT_LIST, EmpathyTpContactList))
+#define EMPATHY_TP_CONTACT_LIST_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CONTACT_LIST, EmpathyTpContactListClass))
+#define EMPATHY_IS_TP_CONTACT_LIST(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CONTACT_LIST))
+#define EMPATHY_IS_TP_CONTACT_LIST_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CONTACT_LIST))
+#define EMPATHY_TP_CONTACT_LIST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CONTACT_LIST, EmpathyTpContactListClass))
+
+typedef struct _EmpathyTpContactList      EmpathyTpContactList;
+typedef struct _EmpathyTpContactListClass EmpathyTpContactListClass;
+typedef struct _EmpathyTpContactListPriv  EmpathyTpContactListPriv;
+
+struct _EmpathyTpContactList {
+       GObject      parent;
+};
+
+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);
+GossipContact *        empathy_tp_contact_list_get_user         (EmpathyTpContactList *list);
+GossipContact *        empathy_tp_contact_list_get_from_id      (EmpathyTpContactList *list,
+                                                                const gchar          *id);
+GossipContact *        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);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_TP_CONTACT_LIST_H__ */
index 68be90c..51015f3 100644 (file)
@@ -30,6 +30,7 @@
 #include "gossip-contact.h"
 #include "gossip-utils.h"
 #include "gossip-debug.h"
+#include "empathy-contact-manager.h"
 
 #define DEBUG_DOMAIN "Contact"
 
@@ -654,6 +655,24 @@ gossip_contact_get_status (GossipContact *contact)
        return _("Offline");
 }
 
+GossipContact *
+gossip_contact_get_user (GossipContact *contact)
+{
+       GossipContactPriv     *priv;
+       EmpathyContactManager *manager;
+       GossipContact         *user_contact;
+
+       g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
+
+       priv = GET_PRIV (contact);
+
+       manager = empathy_contact_manager_new ();
+       user_contact = empathy_contact_manager_get_user (manager, priv->account);
+       g_object_unref (manager);
+
+       return user_contact;
+}
+
 gboolean
 gossip_contact_equal (gconstpointer v1,
                      gconstpointer v2)
index 0b20329..3dc9bc7 100644 (file)
@@ -89,6 +89,7 @@ gboolean           gossip_contact_is_online                 (GossipContact
 gboolean           gossip_contact_is_in_group               (GossipContact      *contact,
                                                             const gchar        *group);
 const gchar *      gossip_contact_get_status                (GossipContact      *contact);
+GossipContact *    gossip_contact_get_user                  (GossipContact      *contact);
 gboolean           gossip_contact_equal                     (gconstpointer       v1,
                                                             gconstpointer       v2);
 guint              gossip_contact_hash                      (gconstpointer       key);
index b8cc59a..6ab70ce 100644 (file)
@@ -432,23 +432,6 @@ gossip_account_equal (gconstpointer a,
        return g_str_equal (name_a, name_b);
 }
 
-GossipContact *
-gossip_get_own_contact_from_contact (GossipContact  *contact)
-{
-       EmpathyContactManager *manager;
-       McAccount             *account;
-       GossipContact         *own_contact;
-
-       g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
-
-       manager = empathy_contact_manager_new ();
-       account = gossip_contact_get_account (contact);
-       own_contact = empathy_contact_manager_get_own (manager, account);
-       g_object_unref (manager);
-
-       return own_contact;
-}
-
 MissionControl *
 gossip_mission_control_new (void)
 {
index e1e9bae..052dfb7 100644 (file)
@@ -88,7 +88,6 @@ gboolean     gossip_g_value_equal                  (const GValue    *value1,
 guint        gossip_account_hash                    (gconstpointer   key);
 gboolean     gossip_account_equal                   (gconstpointer   a,
                                                     gconstpointer   b);
-GossipContact * gossip_get_own_contact_from_contact (GossipContact  *contact);
 MissionControl *gossip_mission_control_new (void);
 
 G_END_DECLS
index 7750b0b..2877665 100644 (file)
@@ -134,14 +134,14 @@ new_channel_cb (EmpathyChandler *chandler,
 
        if (tp_chan->handle_type == TP_HANDLE_TYPE_CONTACT) {
                EmpathyContactManager *manager;
-               EmpathyContactList    *list;
+               EmpathyTpContactList  *list;
                GossipContact         *contact;
                GossipPrivateChat     *chat;
 
                /* We have a new private chat channel */
                manager = empathy_contact_manager_new ();
                list = empathy_contact_manager_get_list (manager, account);
-               contact = empathy_contact_list_get_from_handle (list, tp_chan->handle);
+               contact = empathy_tp_contact_list_get_from_handle (list, tp_chan->handle);
 
                chat = gossip_private_chat_new_with_channel (contact, tp_chan);
                g_object_weak_ref (G_OBJECT (chat),
@@ -157,6 +157,24 @@ new_channel_cb (EmpathyChandler *chandler,
                g_object_unref (chat);
                g_object_unref (manager);
        }
+       if (tp_chan->handle_type == TP_HANDLE_TYPE_ROOM) {
+#if 0
+               GossipGroupChat *chat;
+
+               /* We have a new group chat channel */
+               chat = gossip_group_chat_new (account, tp_chan);
+               g_object_weak_ref (G_OBJECT (chat),
+                                  (GWeakNotify) chat_finalized_cb,
+                                  NULL);
+
+               exit_timeout_stop ();
+               chat_count++;
+
+               gossip_chat_present (GOSSIP_CHAT (chat));
+
+               g_object_unref (chat);
+#endif
+       }
 
 OUT:
        g_free (id);