Add support for blinking when there is an event. Make use of EmpathyIdle
authorXavier Claessens <xclaesse@gmail.com>
Wed, 6 Jun 2007 09:10:23 +0000 (09:10 +0000)
committerXavier Claessens <xclaesse@src.gnome.org>
Wed, 6 Jun 2007 09:10:23 +0000 (09:10 +0000)
2007-06-06 Xavier Claessens  <xclaesse@gmail.com>

* libempathy-gtk/empathy-status-icon.c: Add support for blinking when
there is an event. Make use of EmpathyIdle for presence handling. Add
an event when a contact requets subscription.

* libempathy-gtk/gossip-contact-list-store.c:
* libempathy-gtk/gossip-contact-list-view.c:
* libempathy/empathy-contact-manager.c:
* libempathy/empathy-tp-contact-list.c:
* libempathy/empathy-tp-chatroom.c:
* libempathy/empathy-contact-list.c:
* libempathy/empathy-contact-list.h: get_contacts() is renamed to
get_members(). Adding a signal and a method for local-pending with
contacts with the message. Rework completely the contact-list handling in
EmpathyTpContactList to follow tp spec.

* libempathy/empathy-idle.c:
* libempathy/empathy-idle.h: Add properties for the state and the
status message. EmpathyIdle is now a singleton to manager self presence.

* TODO: Updated.

svn path=/trunk/; revision=123

13 files changed:
ChangeLog
TODO
libempathy-gtk/empathy-status-icon.c
libempathy-gtk/gossip-contact-list-store.c
libempathy-gtk/gossip-contact-list-view.c
libempathy/empathy-contact-list.c
libempathy/empathy-contact-list.h
libempathy/empathy-contact-manager.c
libempathy/empathy-idle.c
libempathy/empathy-idle.h
libempathy/empathy-marshal.list
libempathy/empathy-tp-chatroom.c
libempathy/empathy-tp-contact-list.c

index c54d06c..c7b9ecb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2007-06-06 Xavier Claessens  <xclaesse@gmail.com>
+
+       * libempathy-gtk/empathy-status-icon.c: Add support for blinking when
+       there is an event. Make use of EmpathyIdle for presence handling. Add
+       an event when a contact requets subscription.
+
+       * libempathy-gtk/gossip-contact-list-store.c:
+       * libempathy-gtk/gossip-contact-list-view.c:
+       * libempathy/empathy-contact-manager.c:
+       * libempathy/empathy-tp-contact-list.c:
+       * libempathy/empathy-tp-chatroom.c:
+       * libempathy/empathy-contact-list.c:
+       * libempathy/empathy-contact-list.h: get_contacts() is renamed to
+       get_members(). Adding a signal and a method for local-pending with
+       contacts with the message. Rework completely the contact-list handling in
+       EmpathyTpContactList to follow tp spec.
+
+       * libempathy/empathy-idle.c:
+       * libempathy/empathy-idle.h: Add properties for the state and the 
+       status message. EmpathyIdle is now a singleton to manager self presence.
+
+       * TODO: Updated.
+
 2007-06-03 Xavier Claessens  <xclaesse@gmail.com>
 
        * libempathy-gtk/gossip-accounts-dialog.glade: Set use_stock for
diff --git a/TODO b/TODO
index f6d5d9c..5be759c 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,15 +1,13 @@
 Things you can do if you want to help:
 
- - Rename all files and functions name to use the empathy namespace.
+ - Rename all files and functions name to use the empathy namespace. Bug #444490.
  - Porting gossip-account-widget-*.{c,h} from gossip project (Guillaume is already working on IRC widget).
  - Porting various UI widgets from gossip to libempathy-gtk for contact info, adding contact, personal info, etc.
  - GtkWidget-ify gossip widgets imported in libempathy-gtk. Actually most window/dialog do not inherit from GtkWindow/GtkDialog. Need to create a glade catalog.
- - Fix setting subscription for contacts in EmpathyTpContactList.
- - Filter channels before dispatching them. For example we need a GtkStatusIcon that blink when an event arrives (text/voip/ft channel) and tells the MC to dispatch the channel only when the user clicked the icon. Like in gossip. For that we need a filter DBus API in MC, not yet written.
+ - Filter channels before dispatching them. For example we need a GtkStatusIcon that blink when an event arrives (text/voip/ft channel) and tells the MC to dispatch the channel only when the user clicked the icon. Like in gossip. For that we need a filter DBus API in MC, not yet written, a draft spec is proposed on the telepathy ML.
  - Make use of NetworkManager to set the presence
  - Remove Quit option everywhere, empathy is a session service and shouldn't be leaved.
  - Add sound events
- - Import loggin system from gossip
  - Add register capability in GossipAccountsDialog if the profile says it's supported.
  - Testing and Bugfixing.
 
@@ -19,4 +17,4 @@ SoC projects:
  
 If you want to contribute you can ask for information at
  - #telepathy on freenode
- - Telepathy's mailing list.
+ - Telepathy's mailing list: telepathy@lists.freedesktop.org
index 01a5424..5c36408 100644 (file)
 
 #include <gtk/gtk.h>
 #include <glade/glade.h>
+#include <glib/gi18n.h>
 
 #include <libmissioncontrol/mission-control.h>
 
+#include <libempathy/empathy-contact-list.h>
+#include <libempathy/empathy-contact-manager.h>
+#include <libempathy/gossip-contact.h>
 #include <libempathy/gossip-debug.h>
 #include <libempathy/gossip-utils.h>
 #include <libempathy/gossip-conf.h>
 
 #define DEBUG_DOMAIN "StatusIcon"
 
+/* Number of ms to wait when blinking */
+#define BLINK_TIMEOUT 500
+
 struct _EmpathyStatusIconPriv {
-       MissionControl *mc;
-       GtkStatusIcon  *icon;
-       EmpathyIdle    *idle;
+       GtkStatusIcon         *icon;
+       EmpathyContactManager *manager;
+       EmpathyIdle           *idle;
+       GList                 *events;
+       guint                  blink_timeout;
+       gboolean               showing_state_icon;
+
+       GtkWindow             *window;
+
+       GtkWidget             *popup_menu;
+       GtkWidget             *show_window_item;
+       GtkWidget             *message_item;
+       GtkWidget             *status_item;
+};
 
-       GtkWindow      *window;
+typedef struct _StatusIconEvent StatusIconEvent;
 
-       GtkWidget      *popup_menu;
-       GtkWidget      *show_window_item;
-       GtkWidget      *message_item;
-       GtkWidget      *status_item;
+typedef void (*EventActivatedFunc) (StatusIconEvent *event);
+
+struct _StatusIconEvent {
+       gchar              *icon_name;
+       gchar              *message;
+       EventActivatedFunc  func;
+       gpointer            user_data;
 };
 
-static void     empathy_status_icon_class_init  (EmpathyStatusIconClass *klass);
-static void     empathy_status_icon_init        (EmpathyStatusIcon      *icon);
-static void     status_icon_finalize            (GObject                *object);
-static void     status_icon_presence_changed_cb (MissionControl         *mc,
-                                                McPresence              state,
-                                                EmpathyStatusIcon      *icon);
-static void     status_icon_toggle_visibility   (EmpathyStatusIcon      *icon);
-static void     status_icon_activate_cb         (GtkStatusIcon          *status_icon,
-                                                EmpathyStatusIcon      *icon);
-static gboolean status_icon_delete_event_cb     (GtkWidget              *widget,
-                                                GdkEvent               *event,
-                                                EmpathyStatusIcon      *icon);
-static void     status_icon_popup_menu_cb       (GtkStatusIcon          *status_icon,
-                                                guint                   button,
-                                                guint                   activate_time,
-                                                EmpathyStatusIcon      *icon);
-static void     status_icon_create_menu         (EmpathyStatusIcon      *icon);
-static void     status_icon_new_message_cb      (GtkWidget              *widget,
-                                                EmpathyStatusIcon      *icon);
-static void     status_icon_quit_cb             (GtkWidget              *window,
-                                                EmpathyStatusIcon      *icon);
-static void     status_icon_show_hide_window_cb (GtkWidget              *widget,
-                                                EmpathyStatusIcon      *icon);
+
+static void       empathy_status_icon_class_init  (EmpathyStatusIconClass *klass);
+static void       empathy_status_icon_init        (EmpathyStatusIcon      *icon);
+static void       status_icon_finalize            (GObject                *object);
+static void       status_icon_idle_notify_cb      (EmpathyIdle            *idle,
+                                                  GParamSpec             *param,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_update_tooltip      (EmpathyStatusIcon      *icon);
+static void       status_icon_set_from_state      (EmpathyStatusIcon      *icon);
+static void       status_icon_toggle_visibility   (EmpathyStatusIcon      *icon);
+static void       status_icon_activate_cb         (GtkStatusIcon          *status_icon,
+                                                  EmpathyStatusIcon      *icon);
+static gboolean   status_icon_delete_event_cb     (GtkWidget              *widget,
+                                                  GdkEvent               *event,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_popup_menu_cb       (GtkStatusIcon          *status_icon,
+                                                  guint                   button,
+                                                  guint                   activate_time,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_create_menu         (EmpathyStatusIcon      *icon);
+static void       status_icon_new_message_cb      (GtkWidget              *widget,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_quit_cb             (GtkWidget              *window,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_show_hide_window_cb (GtkWidget              *widget,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_local_pending_cb    (EmpathyContactManager  *manager,
+                                                  GossipContact          *contact,
+                                                  gchar                  *message,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_event_subscribe_cb  (StatusIconEvent        *event);
+static StatusIconEvent * status_icon_event_new    (EmpathyStatusIcon      *icon,
+                                                  const gchar            *icon_name,
+                                                  const gchar            *message);
+static void       status_icon_event_remove        (EmpathyStatusIcon      *icon,
+                                                  StatusIconEvent        *event);
+static gboolean   status_icon_event_timeout_cb    (EmpathyStatusIcon      *icon);
+static void       status_icon_event_free          (StatusIconEvent        *event);
 
 G_DEFINE_TYPE (EmpathyStatusIcon, empathy_status_icon, G_TYPE_OBJECT);
 
@@ -99,29 +135,43 @@ static void
 empathy_status_icon_init (EmpathyStatusIcon *icon)
 {
        EmpathyStatusIconPriv *priv;
-       McPresence             state;
+       GList                 *pending, *l;
 
        priv = GET_PRIV (icon);
 
        priv->icon = gtk_status_icon_new ();
-       priv->mc = gossip_mission_control_new ();
        priv->idle = empathy_idle_new ();
+       priv->manager = empathy_contact_manager_new ();
+       priv->showing_state_icon = TRUE;
 
        status_icon_create_menu (icon);
+       status_icon_set_from_state (icon);
+       status_icon_update_tooltip (icon);
 
-       state = mission_control_get_presence_actual (priv->mc, NULL);
-       status_icon_presence_changed_cb (priv->mc, state, icon);
-       
-       dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
-                                    "PresenceStatusActual",
-                                    G_CALLBACK (status_icon_presence_changed_cb),
-                                    icon, NULL);
+       g_signal_connect (priv->idle, "notify",
+                         G_CALLBACK (status_icon_idle_notify_cb),
+                         icon);
        g_signal_connect (priv->icon, "activate",
                          G_CALLBACK (status_icon_activate_cb),
                          icon);
        g_signal_connect (priv->icon, "popup-menu",
                          G_CALLBACK (status_icon_popup_menu_cb),
                          icon);
+       g_signal_connect (priv->manager, "local-pending",
+                         G_CALLBACK (status_icon_local_pending_cb),
+                         icon);
+
+       pending = empathy_contact_list_get_local_pending (EMPATHY_CONTACT_LIST (priv->manager));
+       for (l = pending; l; l = l->next) {
+               EmpathyContactListInfo *info;
+
+               info = l->data;
+               status_icon_local_pending_cb (priv->manager,
+                                             info->contact,
+                                             info->message,
+                                             icon);
+       }
+       g_list_free (pending);
 }
 
 static void
@@ -131,15 +181,17 @@ status_icon_finalize (GObject *object)
 
        priv = GET_PRIV (object);
 
-       dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc),
-                                       "PresenceStatusActual",
-                                       G_CALLBACK (status_icon_presence_changed_cb),
-                                       object);
+       g_list_foreach (priv->events, (GFunc) status_icon_event_free, NULL);
+       g_list_free (priv->events);
+
+       if (priv->blink_timeout) {
+               g_source_remove (priv->blink_timeout);
+       }
 
-       g_object_unref (priv->mc);
        g_object_unref (priv->icon);
        g_object_unref (priv->window);
        g_object_unref (priv->idle);
+       g_object_unref (priv->manager);
 }
 
 EmpathyStatusIcon *
@@ -174,33 +226,55 @@ empathy_status_icon_new (GtkWindow *window)
 }
 
 static void
-status_icon_presence_changed_cb (MissionControl    *mc,
-                                McPresence         state,
-                                EmpathyStatusIcon *icon)
+status_icon_idle_notify_cb (EmpathyIdle       *idle,
+                           GParamSpec        *param,
+                           EmpathyStatusIcon *icon)
 {
        EmpathyStatusIconPriv *priv;
-       const gchar           *icon_name;
-       gchar                 *status;
 
        priv = GET_PRIV (icon);
 
-       icon_name = gossip_icon_name_for_presence_state (state);
-       status = mission_control_get_presence_message_actual (priv->mc, NULL);
-       if (G_STR_EMPTY (status)) {
-               g_free (status);
-               status = g_strdup (gossip_presence_state_get_default_status (state));
+       if (priv->showing_state_icon) {
+               status_icon_set_from_state (icon);
        }
 
-       gtk_status_icon_set_from_icon_name (priv->icon, icon_name);
-       gtk_status_icon_set_tooltip (priv->icon, status);
+       status_icon_update_tooltip (icon);
+}
+
+static void
+status_icon_update_tooltip (EmpathyStatusIcon *icon)
+{
+       EmpathyStatusIconPriv *priv;
+       const gchar           *tooltip = NULL;
 
-       g_free (status);
+       priv = GET_PRIV (icon);
 
-       if (state < MC_PRESENCE_AVAILABLE) {
-               gtk_widget_set_sensitive (priv->message_item, FALSE);
-       } else {
-               gtk_widget_set_sensitive (priv->message_item, TRUE);
+       if (priv->events) {
+               StatusIconEvent *event;
+
+               event = priv->events->data;
+               tooltip = event->message;
+       }
+
+       if (!tooltip) {
+               tooltip = empathy_idle_get_status (priv->idle);
        }
+
+       gtk_status_icon_set_tooltip (priv->icon, tooltip);      
+}
+
+static void
+status_icon_set_from_state (EmpathyStatusIcon *icon)
+{
+       EmpathyStatusIconPriv *priv;
+       McPresence             state;
+       const gchar           *icon_name;
+
+       priv = GET_PRIV (icon);
+
+       state = empathy_idle_get_state (priv->idle);
+       icon_name = gossip_icon_name_for_presence_state (state);
+       gtk_status_icon_set_from_icon_name (priv->icon, icon_name);
 }
 
 static void
@@ -240,7 +314,15 @@ static void
 status_icon_activate_cb (GtkStatusIcon     *status_icon,
                         EmpathyStatusIcon *icon)
 {
-       status_icon_toggle_visibility (icon);
+       EmpathyStatusIconPriv *priv;
+
+       priv = GET_PRIV (icon);
+
+       if (priv->events) {
+               status_icon_event_remove (icon, priv->events->data);
+       } else {
+               status_icon_toggle_visibility (icon);
+       }
 }
 
 static gboolean
@@ -343,3 +425,128 @@ status_icon_show_hide_window_cb (GtkWidget         *widget,
        status_icon_toggle_visibility (icon);
 }
 
+static void
+status_icon_local_pending_cb (EmpathyContactManager *manager,
+                             GossipContact         *contact,
+                             gchar                 *message,
+                             EmpathyStatusIcon     *icon)
+{
+       EmpathyStatusIconPriv *priv;
+       StatusIconEvent       *event;
+       gchar                 *str;
+       GList                 *l;
+
+       priv = GET_PRIV (icon);
+
+       for (l = priv->events; l; l = l->next) {
+               if (gossip_contact_equal (contact, ((StatusIconEvent*)l->data)->user_data)) {
+                       return;
+               }
+       }
+
+       str = g_strdup_printf (_("Subscription requested for %s\n"
+                                "Message: %s"),
+                              gossip_contact_get_name (contact),
+                              message);
+
+       event = status_icon_event_new (icon, GTK_STOCK_DIALOG_QUESTION, str);
+       event->user_data = g_object_ref (contact);
+       event->func = status_icon_event_subscribe_cb;
+
+       g_free (str);
+}
+
+static void
+status_icon_event_subscribe_cb (StatusIconEvent *event)
+{
+       GossipContact *contact;
+
+       contact = GOSSIP_CONTACT (event->user_data);
+
+       g_object_unref (contact);
+}
+
+static StatusIconEvent *
+status_icon_event_new (EmpathyStatusIcon *icon,
+                      const gchar       *icon_name,
+                      const gchar       *message)
+{
+       EmpathyStatusIconPriv *priv;
+       StatusIconEvent       *event;
+
+       priv = GET_PRIV (icon);
+
+       event = g_slice_new0 (StatusIconEvent);
+       event->icon_name = g_strdup (icon_name);        
+       event->message = g_strdup (message);
+
+       priv->events = g_list_append (priv->events, event);
+       if (!priv->blink_timeout) {
+               priv->blink_timeout = g_timeout_add (BLINK_TIMEOUT,
+                                                    (GSourceFunc) status_icon_event_timeout_cb,
+                                                    icon);
+               status_icon_event_timeout_cb (icon);
+       }
+
+       return event;
+}
+
+static void
+status_icon_event_remove (EmpathyStatusIcon *icon,
+                         StatusIconEvent   *event)
+{
+       EmpathyStatusIconPriv *priv;
+
+       priv = GET_PRIV (icon);
+
+       if (event->func) {
+               event->func (event);
+       }
+       priv->events = g_list_remove (priv->events, event);
+       status_icon_event_free (event);
+       status_icon_update_tooltip (icon);
+
+       if (priv->events) {
+               return;
+       }
+
+       status_icon_set_from_state (icon);
+       priv->showing_state_icon = TRUE;
+
+       if (priv->blink_timeout) {
+               g_source_remove (priv->blink_timeout);
+               priv->blink_timeout = 0;
+
+       }
+}
+
+static gboolean
+status_icon_event_timeout_cb (EmpathyStatusIcon *icon)
+{
+       EmpathyStatusIconPriv *priv;
+
+       priv = GET_PRIV (icon);
+
+       priv->showing_state_icon = !priv->showing_state_icon;
+
+       if (priv->showing_state_icon) {
+               status_icon_set_from_state (icon);
+       } else {
+               StatusIconEvent *event;
+
+               event = priv->events->data;
+               gtk_status_icon_set_from_icon_name (priv->icon, event->icon_name);
+       }
+       status_icon_update_tooltip (icon);
+
+       return TRUE;
+}
+
+static void
+status_icon_event_free (StatusIconEvent *event)
+{
+       g_free (event->icon_name);
+       g_free (event->message);
+       g_slice_free (StatusIconEvent, event);
+}
+
index f0ff476..4a5b550 100644 (file)
@@ -343,7 +343,7 @@ gossip_contact_list_store_new (EmpathyContactList *list_iface)
        /* Add contacts already created. Do not highlight them. */
        show_active = priv->show_active;
        priv->show_active = FALSE;
-       contacts = empathy_contact_list_get_contacts (priv->list);
+       contacts = empathy_contact_list_get_members (priv->list);
        for (l = contacts; l; l = l->next) {
                GossipContact *contact;
 
@@ -401,7 +401,7 @@ gossip_contact_list_store_set_show_offline (GossipContactListStore *store,
        /* Disable temporarily. */
        priv->show_active = FALSE;
 
-       contacts = empathy_contact_list_get_contacts (priv->list);
+       contacts = empathy_contact_list_get_members (priv->list);
        for (l = contacts; l; l = l->next) {
                GossipContact *contact;
 
@@ -654,7 +654,7 @@ gossip_contact_list_store_set_contact_groups_func (GossipContactListStore  *stor
         * to call himself gossip_contact_list_store_update_contact_groups ()
         * when needed. If func is NULL we come back to default.
         */
-       contacts = empathy_contact_list_get_contacts (priv->list);
+       contacts = empathy_contact_list_get_members (priv->list);
        for (l = contacts; l; l = l->next) {
                GossipContact *contact;
 
index 3cad782..368ba8b 100644 (file)
@@ -1383,7 +1383,7 @@ contact_list_view_filter_show_group (GossipContactListView *view,
         * show exists in it.
         */
        list = gossip_contact_list_store_get_list_iface (priv->store);
-       contacts = empathy_contact_list_get_contacts (list);
+       contacts = empathy_contact_list_get_members (list);
        for (l = contacts; l && !show_group; l = l->next) {
                if (!gossip_contact_is_in_group (l->data, group)) {
                        continue;
index b7d0201..c55d0ab 100644 (file)
@@ -23,6 +23,7 @@
 #include "config.h"
 
 #include "empathy-contact-list.h"
+#include "empathy-marshal.h"
 
 static void contact_list_base_init (gpointer klass);
 
@@ -70,10 +71,49 @@ contact_list_base_init (gpointer klass)
                              G_TYPE_NONE,
                              1, GOSSIP_TYPE_CONTACT);
 
+               g_signal_new ("local-pending",
+                             G_TYPE_FROM_CLASS (klass),
+                             G_SIGNAL_RUN_LAST,
+                             0,
+                             NULL, NULL,
+                             empathy_marshal_VOID__OBJECT_STRING,
+                             G_TYPE_NONE,
+                             2, GOSSIP_TYPE_CONTACT, G_TYPE_STRING);
+
                initialized = TRUE;
        }
 }
 
+EmpathyContactListInfo *
+empathy_contact_list_info_new (GossipContact *contact,
+                              const gchar   *message)
+{
+       EmpathyContactListInfo *info;
+
+       g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
+
+       info = g_slice_new0 (EmpathyContactListInfo);
+       info->contact = g_object_ref (contact);
+       info->message = g_strdup (message);
+
+       return info;
+}                             
+
+void
+empathy_contact_list_info_free (EmpathyContactListInfo *info)
+{
+       if (!info) {
+               return;
+       }
+
+       if (info->contact) {
+               g_object_unref (info->contact);
+       }
+       g_free (info->message);
+
+       g_slice_free (EmpathyContactListInfo, info);
+}
+
 void
 empathy_contact_list_setup (EmpathyContactList *list)
 {
@@ -122,12 +162,24 @@ empathy_contact_list_remove (EmpathyContactList *list,
 }
 
 GList *
-empathy_contact_list_get_contacts (EmpathyContactList *list)
+empathy_contact_list_get_members (EmpathyContactList *list)
+{
+       g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL);
+
+       if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_members) {
+               return EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_members (list);
+       }
+
+       return NULL;
+}
+
+GList *
+empathy_contact_list_get_local_pending (EmpathyContactList *list)
 {
        g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL);
 
-       if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_contacts) {
-               return EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_contacts (list);
+       if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_local_pending) {
+               return EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_local_pending (list);
        }
 
        return NULL;
index 02ec318..09f5462 100644 (file)
@@ -37,33 +37,43 @@ G_BEGIN_DECLS
 typedef struct _EmpathyContactList      EmpathyContactList;
 typedef struct _EmpathyContactListIface EmpathyContactListIface;
 
+typedef struct {
+       GossipContact *contact;
+       gchar         *message;
+} EmpathyContactListInfo;
+
 struct _EmpathyContactListIface {
        GTypeInterface   base_iface;
 
        /* 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);
+       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_members)       (EmpathyContactList *list);
+       GList *         (*get_local_pending) (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);
+GType                   empathy_contact_list_get_type          (void) G_GNUC_CONST;
+EmpathyContactListInfo *empathy_contact_list_info_new          (GossipContact          *contact,
+                                                               const gchar            *message);
+void                    empathy_contact_list_info_free         (EmpathyContactListInfo *info);
+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_members       (EmpathyContactList     *list);
+GList *                 empathy_contact_list_get_local_pending (EmpathyContactList     *list);
 
 G_END_DECLS
 
index fa62da9..5e5f09a 100644 (file)
@@ -65,7 +65,8 @@ static void           contact_manager_add                  (EmpathyContactList
 static void           contact_manager_remove               (EmpathyContactList              *manager,
                                                            GossipContact                   *contact,
                                                            const gchar                     *message);
-static GList *        contact_manager_get_contacts         (EmpathyContactList              *manager);
+static GList *        contact_manager_get_members          (EmpathyContactList              *manager);
+static GList *        contact_manager_get_local_pending    (EmpathyContactList              *manager);
 static void           contact_manager_setup_foreach        (McAccount                       *account,
                                                            EmpathyTpContactList            *list,
                                                            EmpathyContactManager           *manager);
@@ -80,6 +81,10 @@ static void           contact_manager_added_cb             (EmpathyTpContactList
 static void           contact_manager_removed_cb           (EmpathyTpContactList            *list,
                                                            GossipContact                   *contact,
                                                            EmpathyContactManager           *manager);
+static void           contact_manager_local_pending_cb     (EmpathyTpContactList            *list,
+                                                           GossipContact                   *contact,
+                                                           const gchar                     *message,
+                                                           EmpathyContactManager           *manager);
 static void           contact_manager_destroy_cb           (EmpathyTpContactList            *list,
                                                            EmpathyContactManager           *manager);
 static void           contact_manager_rename_group_foreach (McAccount                       *account,
@@ -88,7 +93,10 @@ static void           contact_manager_rename_group_foreach (McAccount
 static void           contact_manager_get_groups_foreach   (McAccount                       *account,
                                                            EmpathyTpContactList            *list,
                                                            GList                          **all_groups);
-static void           contact_manager_get_contacts_foreach (McAccount                       *account,
+static void           contact_manager_get_members_foreach  (McAccount                       *account,
+                                                           EmpathyTpContactList            *list,
+                                                           GList                          **contacts);
+static void           contact_manager_get_local_pending_foreach (McAccount                  *account,
                                                            EmpathyTpContactList            *list,
                                                            GList                          **contacts);
 static void           contact_manager_status_changed_cb    (MissionControl                  *mc,
@@ -115,11 +123,12 @@ empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
 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;
+       iface->setup             = contact_manager_setup;
+       iface->find              = contact_manager_find;
+       iface->add               = contact_manager_add;
+       iface->remove            = contact_manager_remove;
+       iface->get_members       = contact_manager_get_members;
+       iface->get_local_pending = contact_manager_get_local_pending;
 }
 
 static void
@@ -269,7 +278,7 @@ contact_manager_remove (EmpathyContactList *manager,
 }
 
 static GList *
-contact_manager_get_contacts (EmpathyContactList *manager)
+contact_manager_get_members (EmpathyContactList *manager)
 {
        EmpathyContactManagerPriv *priv;
        GList                     *contacts = NULL;
@@ -279,12 +288,29 @@ contact_manager_get_contacts (EmpathyContactList *manager)
        priv = GET_PRIV (manager);
 
        g_hash_table_foreach (priv->lists,
-                             (GHFunc) contact_manager_get_contacts_foreach,
+                             (GHFunc) contact_manager_get_members_foreach,
                              &contacts);
 
        return contacts;
 }
 
+static GList *
+contact_manager_get_local_pending (EmpathyContactList *manager)
+{
+       EmpathyContactManagerPriv *priv;
+       GList                     *pending = NULL;
+
+       g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
+
+       priv = GET_PRIV (manager);
+
+       g_hash_table_foreach (priv->lists,
+                             (GHFunc) contact_manager_get_local_pending_foreach,
+                             &pending);
+
+       return pending;
+}
+
 EmpathyTpContactList *
 empathy_contact_manager_get_list (EmpathyContactManager *manager,
                                  McAccount             *account)
@@ -435,6 +461,9 @@ contact_manager_add_account (EmpathyContactManager *manager,
        g_signal_connect (list, "contact-removed",
                          G_CALLBACK (contact_manager_removed_cb),
                          manager);
+       g_signal_connect (list, "local-pending",
+                         G_CALLBACK (contact_manager_local_pending_cb),
+                         manager);
        g_signal_connect (list, "destroy",
                          G_CALLBACK (contact_manager_destroy_cb),
                          manager);
@@ -460,6 +489,15 @@ contact_manager_removed_cb (EmpathyTpContactList  *list,
        g_signal_emit_by_name (manager, "contact-removed", contact);
 }
 
+static void
+contact_manager_local_pending_cb (EmpathyTpContactList  *list,
+                                 GossipContact         *contact,
+                                 const gchar           *message,
+                                 EmpathyContactManager *manager)
+{
+       g_signal_emit_by_name (manager, "local-pending", contact, message);
+}
+
 static void
 contact_manager_destroy_cb (EmpathyTpContactList  *list,
                            EmpathyContactManager *manager)
@@ -481,6 +519,9 @@ contact_manager_destroy_cb (EmpathyTpContactList  *list,
        g_signal_handlers_disconnect_by_func (list,
                                              contact_manager_removed_cb,
                                              manager);
+       g_signal_handlers_disconnect_by_func (list,
+                                             contact_manager_local_pending_cb,
+                                             manager);
        g_signal_handlers_disconnect_by_func (list,
                                              contact_manager_destroy_cb,
                                              manager);
@@ -520,13 +561,24 @@ contact_manager_get_groups_foreach (McAccount             *account,
 }
 
 static void
-contact_manager_get_contacts_foreach (McAccount             *account,
-                                     EmpathyTpContactList  *list,
-                                     GList                **contacts)
+contact_manager_get_members_foreach (McAccount             *account,
+                                    EmpathyTpContactList  *list,
+                                    GList                **contacts)
+{
+       GList *l;
+
+       l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list));
+       *contacts = g_list_concat (*contacts, l);
+}
+
+static void
+contact_manager_get_local_pending_foreach (McAccount             *account,
+                                          EmpathyTpContactList  *list,
+                                          GList                **contacts)
 {
        GList *l;
 
-       l = empathy_contact_list_get_contacts (EMPATHY_CONTACT_LIST (list));
+       l = empathy_contact_list_get_local_pending (EMPATHY_CONTACT_LIST (list));
        *contacts = g_list_concat (*contacts, l);
 }
 
index 6b9cfb3..f9b50bd 100644 (file)
@@ -27,8 +27,6 @@
 
 #include <libtelepathy/tp-helpers.h>
 
-#include <libmissioncontrol/mission-control.h>
-
 #include "empathy-idle.h"
 #include "gossip-utils.h" 
 #include "gossip-debug.h"
@@ -49,6 +47,9 @@ struct _EmpathyIdlePriv {
        MissionControl *mc;
        DBusGProxy     *gs_proxy;
        gboolean        is_idle;
+       McPresence      state;
+       McPresence      slack_state;
+       gchar          *status;
        McPresence      saved_state;
        gchar          *saved_status;
        guint           ext_away_timeout;
@@ -57,6 +58,17 @@ struct _EmpathyIdlePriv {
 static void     empathy_idle_class_init      (EmpathyIdleClass *klass);
 static void     empathy_idle_init            (EmpathyIdle      *idle);
 static void     idle_finalize                (GObject          *object);
+static void     idle_get_property            (GObject          *object,
+                                             guint             param_id,
+                                             GValue           *value,
+                                             GParamSpec       *pspec);
+static void     idle_set_property            (GObject          *object,
+                                             guint             param_id,
+                                             const GValue     *value,
+                                             GParamSpec       *pspec);
+static void     idle_presence_changed_cb     (MissionControl   *mc,
+                                             McPresence        state,
+                                             EmpathyIdle      *idle);
 static void     idle_session_idle_changed_cb (DBusGProxy       *gs_proxy,
                                              gboolean          is_idle,
                                              EmpathyIdle      *idle);
@@ -64,7 +76,12 @@ static void     idle_ext_away_start          (EmpathyIdle      *idle);
 static void     idle_ext_away_stop           (EmpathyIdle      *idle);
 static gboolean idle_ext_away_cb             (EmpathyIdle      *idle);
 
-//static guint signals[LAST_SIGNAL];
+enum {
+       PROP_0,
+       PROP_STATE,
+       PROP_STATUS,
+       PROP_SLACK_STATE
+};
 
 G_DEFINE_TYPE (EmpathyIdle, empathy_idle, G_TYPE_OBJECT)
 
@@ -74,6 +91,34 @@ empathy_idle_class_init (EmpathyIdleClass *klass)
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
        object_class->finalize = idle_finalize;
+       object_class->get_property = idle_get_property;
+       object_class->set_property = idle_set_property;
+
+       g_object_class_install_property (object_class,
+                                        PROP_STATE,
+                                        g_param_spec_uint ("state",
+                                                           "state",
+                                                           "state",
+                                                           MC_PRESENCE_UNSET,
+                                                           LAST_MC_PRESENCE,
+                                                           MC_PRESENCE_AVAILABLE,
+                                                           G_PARAM_READWRITE));
+       g_object_class_install_property (object_class,
+                                        PROP_STATUS,
+                                        g_param_spec_string ("status",
+                                                             "status",
+                                                             "status",
+                                                             NULL,
+                                                             G_PARAM_READWRITE));
+       g_object_class_install_property (object_class,
+                                        PROP_SLACK_STATE,
+                                        g_param_spec_uint ("slack-state",
+                                                           "slack-state",
+                                                           "slack-state",
+                                                           MC_PRESENCE_UNSET,
+                                                           LAST_MC_PRESENCE,
+                                                           MC_PRESENCE_UNSET,
+                                                           G_PARAM_READWRITE));
 
        g_type_class_add_private (object_class, sizeof (EmpathyIdlePriv));
 }
@@ -87,6 +132,8 @@ empathy_idle_init (EmpathyIdle *idle)
 
        priv->is_idle = FALSE;
        priv->mc = gossip_mission_control_new ();
+       priv->state = mission_control_get_presence_actual (priv->mc, NULL);
+       priv->status = mission_control_get_presence_message_actual (priv->mc, NULL);
        priv->gs_proxy = dbus_g_proxy_new_for_name (tp_get_bus (),
                                                    "org.gnome.ScreenSaver",
                                                    "/org/gnome/ScreenSaver",
@@ -102,6 +149,10 @@ empathy_idle_init (EmpathyIdle *idle)
        dbus_g_proxy_connect_signal (priv->gs_proxy, "SessionIdleChanged",
                                     G_CALLBACK (idle_session_idle_changed_cb),
                                     idle, NULL);
+       dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
+                                    "PresenceStatusActual",
+                                    G_CALLBACK (idle_presence_changed_cb),
+                                    idle, NULL);
 }
 
 static void
@@ -111,6 +162,7 @@ idle_finalize (GObject *object)
 
        priv = GET_PRIV (object);
 
+       g_free (priv->status);
        g_free (priv->saved_status);
        g_object_unref (priv->mc);
 
@@ -121,6 +173,62 @@ idle_finalize (GObject *object)
        idle_ext_away_stop (EMPATHY_IDLE (object));
 }
 
+static void
+idle_get_property (GObject    *object,
+                  guint       param_id,
+                  GValue     *value,
+                  GParamSpec *pspec)
+{
+       EmpathyIdlePriv *priv;
+       EmpathyIdle     *idle;
+
+       priv = GET_PRIV (object);
+       idle = EMPATHY_IDLE (object);
+
+       switch (param_id) {
+       case PROP_STATE:
+               g_value_set_uint (value, empathy_idle_get_state (idle));
+               break;
+       case PROP_STATUS:
+               g_value_set_string (value, empathy_idle_get_status (idle));
+               break;
+       case PROP_SLACK_STATE:
+               g_value_set_uint (value, empathy_idle_get_slack_state (idle));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+               break;
+       };
+}
+
+static void
+idle_set_property (GObject      *object,
+                  guint         param_id,
+                  const GValue *value,
+                  GParamSpec   *pspec)
+{
+       EmpathyIdlePriv *priv;
+       EmpathyIdle     *idle;
+
+       priv = GET_PRIV (object);
+       idle = EMPATHY_IDLE (object);
+
+       switch (param_id) {
+       case PROP_STATE:
+               empathy_idle_set_state (idle, g_value_get_uint (value));
+               break;
+       case PROP_STATUS:
+               empathy_idle_set_status (idle, g_value_get_string (value));
+               break;
+       case PROP_SLACK_STATE:
+               empathy_idle_set_slack_state (idle, g_value_get_uint (value));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+               break;
+       };
+}
+
 EmpathyIdle *
 empathy_idle_new (void)
 {
@@ -136,6 +244,99 @@ empathy_idle_new (void)
        return idle;
 }
 
+McPresence
+empathy_idle_get_state (EmpathyIdle *idle)
+{
+       EmpathyIdlePriv *priv;
+
+       priv = GET_PRIV (idle);
+
+       return priv->state;
+}
+
+void
+empathy_idle_set_state (EmpathyIdle *idle,
+                       McPresence   state)
+{
+       EmpathyIdlePriv *priv;
+
+       priv = GET_PRIV (idle);
+
+       mission_control_set_presence (priv->mc,
+                                     state,
+                                     priv->status,
+                                     NULL, NULL);
+}
+
+const gchar *
+empathy_idle_get_status (EmpathyIdle *idle)
+{
+       EmpathyIdlePriv *priv;
+
+       priv = GET_PRIV (idle);
+
+       return priv->status;
+}
+
+void
+empathy_idle_set_status (EmpathyIdle *idle,
+                        const gchar *status)
+{
+       EmpathyIdlePriv *priv;
+
+       priv = GET_PRIV (idle);
+
+       mission_control_set_presence (priv->mc,
+                                     priv->state,
+                                     status,
+                                     NULL, NULL);
+}
+
+McPresence
+empathy_idle_get_slack_state (EmpathyIdle *idle)
+{
+       EmpathyIdlePriv *priv;
+
+       priv = GET_PRIV (idle);
+
+       return priv->slack_state;
+}
+
+void
+empathy_idle_set_slack_state (EmpathyIdle *idle,
+                             McPresence   state)
+{
+       EmpathyIdlePriv *priv;
+
+       priv = GET_PRIV (idle);
+
+       priv->slack_state = state;
+
+       g_object_notify (G_OBJECT (idle), "slack-state");
+}
+
+static void
+idle_presence_changed_cb (MissionControl *mc,
+                         McPresence      state,
+                         EmpathyIdle    *idle)
+{
+       EmpathyIdlePriv *priv;
+
+       priv = GET_PRIV (idle);
+
+       g_free (priv->status);
+       priv->state = state;
+       priv->status = mission_control_get_presence_message_actual (priv->mc, NULL);
+
+       if (G_STR_EMPTY (priv->status)) {
+               g_free (priv->status);
+               priv->status = g_strdup (gossip_presence_state_get_default_status (state));
+       }
+
+       g_object_notify (G_OBJECT (idle), "state");
+       g_object_notify (G_OBJECT (idle), "status");
+}
+
 static void
 idle_session_idle_changed_cb (DBusGProxy  *gs_proxy,
                              gboolean     is_idle,
@@ -150,30 +351,29 @@ idle_session_idle_changed_cb (DBusGProxy  *gs_proxy,
                      is_idle ? "yes" : "no");
 
        if (is_idle && !priv->is_idle) {
-               McPresence new_state = MC_PRESENCE_AWAY;
+               McPresence new_state;
                /* We are now idle, set state to away */
 
-               priv->saved_state = mission_control_get_presence_actual (priv->mc, NULL);
-               priv->saved_status = mission_control_get_presence_message_actual (priv->mc, NULL);
-
-               if (priv->saved_state <= MC_PRESENCE_OFFLINE ||
-                   priv->saved_state == MC_PRESENCE_HIDDEN) {
+               if (priv->state <= MC_PRESENCE_OFFLINE ||
+                   priv->state == MC_PRESENCE_HIDDEN) {
                        /* We are not online so nothing to do here */
                        return;
-               } else if (priv->saved_state == MC_PRESENCE_AWAY ||
-                          priv->saved_state == MC_PRESENCE_EXTENDED_AWAY) {
+               } else if (priv->state == MC_PRESENCE_AWAY ||
+                          priv->state == MC_PRESENCE_EXTENDED_AWAY) {
                        /* User set away manually, when coming back we restore
                         * default presence. */
-                       new_state = priv->saved_state;
+                       new_state = priv->state;
                        priv->saved_state = MC_PRESENCE_AVAILABLE;
                        priv->saved_status = NULL;
+               } else {
+                       new_state = MC_PRESENCE_AWAY;
+                       priv->saved_state = priv->state;
+                       priv->saved_status = g_strdup (priv->status);
                }
 
                gossip_debug (DEBUG_DOMAIN, "Going to autoaway");
-               mission_control_set_presence (priv->mc,
-                                             new_state,
-                                             priv->saved_status,
-                                             NULL, NULL);
+               empathy_idle_set_state (idle, new_state);
+
                idle_ext_away_start (idle);
        } else if (!is_idle && priv->is_idle) {
                /* We are no more idle, restore state */
@@ -187,6 +387,7 @@ idle_session_idle_changed_cb (DBusGProxy  *gs_proxy,
                                              priv->saved_state,
                                              priv->saved_status,
                                              NULL, NULL);
+
                g_free (priv->saved_status);
                priv->saved_status = NULL;
        }
index df8846b..13c5dd2 100644 (file)
@@ -25,6 +25,8 @@
 
 #include <glib.h>
 
+#include <libmissioncontrol/mission-control.h>
+
 G_BEGIN_DECLS
 
 #define EMPATHY_TYPE_IDLE         (empathy_idle_get_type ())
@@ -46,8 +48,17 @@ struct _EmpathyIdleClass {
        GObjectClass parent_class;
 };
 
-GType        empathy_idle_get_type (void) G_GNUC_CONST;
-EmpathyIdle *empathy_idle_new      (void);
+GType        empathy_idle_get_type        (void) G_GNUC_CONST;
+EmpathyIdle *empathy_idle_new             (void);
+McPresence   empathy_idle_get_state       (EmpathyIdle *idle);
+void         empathy_idle_set_state       (EmpathyIdle *idle,
+                                          McPresence   state);
+const gchar *empathy_idle_get_status      (EmpathyIdle *idle);
+void         empathy_idle_set_status      (EmpathyIdle *idle,
+                                          const gchar *message);
+McPresence   empathy_idle_get_slack_state (EmpathyIdle *idle);
+void         empathy_idle_set_slack_state (EmpathyIdle *idle,
+                                          McPresence   state);
 
 G_END_DECLS
 
index f13e9f0..3b36b7b 100644 (file)
@@ -1,8 +1,10 @@
-VOID:POINTER,UINT,UINT,STRING
 VOID:OBJECT,UINT
 VOID:OBJECT,OBJECT
-VOID:INT,STRING
 VOID:OBJECT,OBJECT,UINT
-VOID:UINT,BOOLEAN
 VOID:OBJECT,BOOLEAN
 VOID:OBJECT,STRING,STRING
+VOID:OBJECT,STRING
+VOID:POINTER,UINT,UINT,STRING
+VOID:INT,STRING
+VOID:UINT,BOOLEAN
+
index eb47c9b..232db30 100644 (file)
@@ -72,7 +72,7 @@ static void            tp_chatroom_add                (EmpathyContactList      *
 static void            tp_chatroom_remove             (EmpathyContactList      *list,
                                                       GossipContact           *contact,
                                                       const gchar             *message);
-static GList *         tp_chatroom_get_contacts       (EmpathyContactList      *list);
+static GList *         tp_chatroom_get_members        (EmpathyContactList      *list);
 
 G_DEFINE_TYPE_WITH_CODE (EmpathyTpChatroom, empathy_tp_chatroom, EMPATHY_TYPE_TP_CHAT,
                         G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
@@ -91,11 +91,11 @@ empathy_tp_chatroom_class_init (EmpathyTpChatroomClass *klass)
 static void
 tp_chatroom_iface_init (EmpathyContactListIface *iface)
 {
-       iface->setup = tp_chatroom_setup;
-       iface->find = tp_chatroom_find;
-       iface->add = tp_chatroom_add;
-       iface->remove = tp_chatroom_remove;
-       iface->get_contacts = tp_chatroom_get_contacts;
+       iface->setup       = tp_chatroom_setup;
+       iface->find        = tp_chatroom_find;
+       iface->add         = tp_chatroom_add;
+       iface->remove      = tp_chatroom_remove;
+       iface->get_members = tp_chatroom_get_members;
 }
 
 static void
@@ -339,7 +339,7 @@ tp_chatroom_remove (EmpathyContactList *list,
 }
 
 static GList *
-tp_chatroom_get_contacts (EmpathyContactList *list)
+tp_chatroom_get_members (EmpathyContactList *list)
 {
        EmpathyTpChatroomPriv *priv;
        GArray                *members;
index 7668057..284f511 100644 (file)
@@ -52,12 +52,12 @@ struct _EmpathyTpContactListPriv {
        GossipContact        *user_contact;
        gboolean              setup;
 
-       GossipTelepathyGroup *known;
        GossipTelepathyGroup *publish;
        GossipTelepathyGroup *subscribe;
 
        GHashTable           *groups;
        GHashTable           *contacts;
+       GList                *local_pending;
 
        DBusGProxy           *aliasing_iface;
        DBusGProxy           *avatars_iface;
@@ -67,7 +67,6 @@ struct _EmpathyTpContactListPriv {
 };
 
 typedef enum {
-       TP_CONTACT_LIST_TYPE_KNOWN,
        TP_CONTACT_LIST_TYPE_PUBLISH,
        TP_CONTACT_LIST_TYPE_SUBSCRIBE,
        TP_CONTACT_LIST_TYPE_UNKNOWN,
@@ -103,7 +102,10 @@ static void                   tp_contact_list_add                      (EmpathyC
 static void                   tp_contact_list_remove                   (EmpathyContactList              *list,
                                                                        GossipContact                   *contact,
                                                                        const gchar                     *message);
-static GList *                tp_contact_list_get_contacts             (EmpathyContactList              *list);
+static GList *                tp_contact_list_get_members              (EmpathyContactList              *list);
+static GList *                tp_contact_list_get_local_pending        (EmpathyContactList              *list);
+static void                   tp_contact_list_remove_local_pending     (EmpathyTpContactList            *list,
+                                                                       GossipContact                   *contact);
 static void                   tp_contact_list_contact_removed_foreach  (guint                            handle,
                                                                        GossipContact                   *contact,
                                                                        EmpathyTpContactList            *list);
@@ -120,20 +122,20 @@ static void                   tp_contact_list_newchannel_cb            (DBusGPro
                                                                        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,
+                                                                       GossipTelepathyGroup            *group);
+static void                   tp_contact_list_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,
+static void                   tp_contact_list_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,
+static void                   tp_contact_list_pending_cb               (GossipTelepathyGroup            *group,
                                                                        GArray                          *handles,
                                                                        guint                            actor_handle,
                                                                        guint                            reason,
@@ -173,7 +175,7 @@ static void                   tp_contact_list_group_members_removed_cb (GossipTe
                                                                        guint                            reason,
                                                                        const gchar                     *message,
                                                                        EmpathyTpContactList            *list);
-static void                   tp_contact_list_get_contacts_foreach     (guint                            handle,
+static void                   tp_contact_list_get_members_foreach      (guint                            handle,
                                                                        GossipContact                   *contact,
                                                                        GList                          **contacts);
 static void                   tp_contact_list_get_info                 (EmpathyTpContactList            *list,
@@ -248,11 +250,12 @@ empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass)
 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;
+       iface->setup             = tp_contact_list_setup;
+       iface->find              = tp_contact_list_find;
+       iface->add               = tp_contact_list_add;
+       iface->remove            = tp_contact_list_remove;
+       iface->get_members       = tp_contact_list_get_members;
+       iface->get_local_pending = tp_contact_list_get_local_pending;
 }
 
 static void
@@ -293,15 +296,9 @@ tp_contact_list_finalize (GObject *object)
        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);
        }
@@ -312,6 +309,9 @@ tp_contact_list_finalize (GObject *object)
        g_hash_table_destroy (priv->groups);
        g_hash_table_destroy (priv->contacts);
 
+       g_list_foreach (priv->local_pending, (GFunc) empathy_contact_list_info_free, NULL);
+       g_list_free (priv->local_pending);
+
        G_OBJECT_CLASS (empathy_tp_contact_list_parent_class)->finalize (object);
 }
 
@@ -383,6 +383,7 @@ empathy_tp_contact_list_new (McAccount *account)
                              error ? error->message : "No error given");
                g_clear_error (&error);
        } else {
+               /* FIXME: this adds the handle to the roster */
                priv->user_contact = empathy_tp_contact_list_get_from_handle (list, handle);
        }
 
@@ -490,11 +491,10 @@ tp_contact_list_remove (EmpathyContactList *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)
+tp_contact_list_get_members (EmpathyContactList *list)
 {
        EmpathyTpContactListPriv *priv;
        GList                    *contacts = NULL;
@@ -503,14 +503,25 @@ tp_contact_list_get_contacts (EmpathyContactList *list)
 
        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,
+                             (GHFunc) tp_contact_list_get_members_foreach,
                              &contacts);
 
        return contacts;
 }
 
+static GList *
+tp_contact_list_get_local_pending (EmpathyContactList *list)
+{
+       EmpathyTpContactListPriv *priv;
+
+       g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL);
+
+       priv = GET_PRIV (list);
+
+       return g_list_copy (priv->local_pending);
+}
+
 McAccount *
 empathy_tp_contact_list_get_account (EmpathyTpContactList *list)
 {
@@ -943,87 +954,77 @@ tp_contact_list_newchannel_cb (DBusGProxy           *proxy,
        if (handle_type == TP_HANDLE_TYPE_LIST) {
                TpContactListType list_type;
 
-               list_type = tp_contact_list_get_type (list, new_chan);
+               group = gossip_telepathy_group_new (new_chan, priv->tp_conn);
+
+               list_type = tp_contact_list_get_type (list, group);
                if (list_type == TP_CONTACT_LIST_TYPE_UNKNOWN) {
-                       gossip_debug (DEBUG_DOMAIN, "Unknown contact list channel");
+                       gossip_debug (DEBUG_DOMAIN,
+                                     "Type of contact list channel unknown: %s",
+                                     gossip_telepathy_group_get_name (group));
+
                        g_object_unref (new_chan);
+                       g_object_unref (group);
                        return;
+               } else {
+                       gossip_debug (DEBUG_DOMAIN,
+                                     "New contact list channel of type: %d",
+                                     list_type);
                }
 
-               gossip_debug (DEBUG_DOMAIN, "New contact list channel of type: %d",
-                             list_type);
+               g_signal_connect (group, "members-added",
+                                 G_CALLBACK (tp_contact_list_added_cb),
+                                 list);
+               g_signal_connect (group, "members-removed",
+                                 G_CALLBACK (tp_contact_list_removed_cb),
+                                 list);
 
-               group = gossip_telepathy_group_new (new_chan, priv->tp_conn);
+               members = gossip_telepathy_group_get_members (group);
+               tp_contact_list_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) {
+                       GList *pendings, *l;
 
-               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) {
-                       GList  *members, *l;
-                       GArray *pending;
 
+                       /* Makes no sense to be in remote-pending */
                        g_signal_connect (group, "local-pending",
-                                         G_CALLBACK (tp_contact_list_local_pending_cb),
+                                         G_CALLBACK (tp_contact_list_pending_cb),
                                          list);
 
-                       members = gossip_telepathy_group_get_local_pending_members_with_info (group);
-                       if (!members) {
-                               g_object_unref (new_chan);
-                               return;
+                       pendings = gossip_telepathy_group_get_local_pending_members_with_info (group);
+                       if (pendings) {
+                               GArray *pending;
+
+                               pending = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
+                               for (l = pendings; l; l = l->next) {
+                                       GossipTpGroupInfo *info;
+
+                                       info = l->data;
+
+                                       g_array_insert_val (pending, 0, info->member);
+                                       tp_contact_list_pending_cb (group, pending,
+                                                                   info->actor,
+                                                                   info->reason,
+                                                                   info->message,
+                                                                   list);
+                               }
+                               g_array_free (pending, TRUE);
+                               gossip_telepathy_group_info_list_free (pendings);
                        }
-
-                       pending = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
-                       for (l = members; l; l = l->next) {
-                               GossipTpGroupInfo *info;
-
-                               info = l->data;
-
-                               g_array_insert_val (pending, 0, info->member);
-                               tp_contact_list_local_pending_cb (group, pending,
-                                                                 info->actor,
-                                                                 info->reason,
-                                                                 info->message,
-                                                                 list);
+               }
+               else if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) {
+                       if (priv->subscribe) {
+                               g_object_unref (priv->subscribe);
                        }
-
-                       gossip_telepathy_group_info_list_free (members);
-                       g_array_free (pending, TRUE);
+                       priv->subscribe = group;
+               } else {
+                       g_assert_not_reached ();
                }
        }
        else if (handle_type == TP_HANDLE_TYPE_GROUP) {
@@ -1065,72 +1066,69 @@ tp_contact_list_newchannel_cb (DBusGProxy           *proxy,
 
 static TpContactListType
 tp_contact_list_get_type (EmpathyTpContactList *list,
-                         TpChan               *list_chan)
+                         GossipTelepathyGroup *group)
 {
        EmpathyTpContactListPriv  *priv;
-       GArray                    *handles;
-       gchar                    **handle_name;
        TpContactListType          list_type;
-       GError                    *error = NULL;
+       const gchar               *name;
 
        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) {
+       name = gossip_telepathy_group_get_name (group);
+       if (strcmp (name, "subscribe") == 0) {
                list_type = TP_CONTACT_LIST_TYPE_SUBSCRIBE;
-       } else if (strcmp (*handle_name, "publish") == 0) {
+       } else if (strcmp (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)
+tp_contact_list_added_cb (GossipTelepathyGroup *group,
+                         GArray               *handles,
+                         guint                 actor_handle,
+                         guint                 reason,
+                         const gchar          *message,
+                         EmpathyTpContactList *list)
 {
        EmpathyTpContactListPriv *priv;
        GList                    *added_list, *l;
+       TpContactListType         list_type;
 
        priv = GET_PRIV (list);
 
-       added_list = empathy_tp_contact_list_get_from_handles (list, handles);
+       list_type = tp_contact_list_get_type (list, group);
 
+       added_list = empathy_tp_contact_list_get_from_handles (list, handles);
        for (l = added_list; l; l = l->next) {
-               GossipContact *contact;
+               GossipContact      *contact;
+               GossipSubscription  subscription;
 
                contact = GOSSIP_CONTACT (l->data);
+
+               gossip_debug (DEBUG_DOMAIN, "Contact '%s' added to list type %d",
+                             gossip_contact_get_name (contact),
+                             list_type);
+
+               subscription = gossip_contact_get_subscription (contact);
+               if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) {
+                       subscription |= GOSSIP_SUBSCRIPTION_FROM;
+               }
+               else if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) {
+                       subscription |= GOSSIP_SUBSCRIPTION_TO;
+               }
+
                tp_contact_list_block_contact (list, contact);
-               gossip_contact_set_subscription (contact, GOSSIP_SUBSCRIPTION_BOTH);
+               gossip_contact_set_subscription (contact, subscription);
                tp_contact_list_unblock_contact (list, contact);
 
-               g_signal_emit_by_name (list, "contact-added", contact);
+               if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) {
+                       tp_contact_list_remove_local_pending (list, contact);
+                       g_signal_emit_by_name (list, "contact-added", contact);
+               }
 
                g_object_unref (contact);
        }
@@ -1139,31 +1137,48 @@ tp_contact_list_contact_added_cb (GossipTelepathyGroup *group,
 }
 
 static void
-tp_contact_list_contact_removed_cb (GossipTelepathyGroup *group,
-                                   GArray               *handles,
-                                   guint                 actor_handle,
-                                   guint                 reason,
-                                   const gchar          *message,
-                                   EmpathyTpContactList *list)
+tp_contact_list_removed_cb (GossipTelepathyGroup *group,
+                           GArray               *handles,
+                           guint                 actor_handle,
+                           guint                 reason,
+                           const gchar          *message,
+                           EmpathyTpContactList *list)
 {
        EmpathyTpContactListPriv *priv;
        GList                    *removed_list, *l;
+       TpContactListType         list_type;
 
        priv = GET_PRIV (list);
 
-       removed_list = empathy_tp_contact_list_get_from_handles (list, handles);
+       list_type = tp_contact_list_get_type (list, group);
 
+       removed_list = empathy_tp_contact_list_get_from_handles (list, handles);
        for (l = removed_list; l; l = l->next) {
-               GossipContact *contact;
-               guint          handle;
+               GossipContact      *contact;
+               GossipSubscription  subscription;
 
                contact = GOSSIP_CONTACT (l->data);
 
-               handle = gossip_contact_get_handle (contact);
-               g_hash_table_remove (priv->contacts, GUINT_TO_POINTER (handle));
+               gossip_debug (DEBUG_DOMAIN, "Contact '%s' removed from list type %d",
+                             gossip_contact_get_name (contact),
+                             list_type);
+
+               subscription = gossip_contact_get_subscription (contact);
+               if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) {
+                       subscription &= !GOSSIP_SUBSCRIPTION_FROM;
+               }
+               else if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) {
+                       subscription &= !GOSSIP_SUBSCRIPTION_TO;
+               }
 
-               g_signal_emit_by_name (list, "contact-removed", contact);
+               tp_contact_list_block_contact (list, contact);
+               gossip_contact_set_subscription (contact, subscription);
+               tp_contact_list_unblock_contact (list, contact);
 
+               if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) {
+                       tp_contact_list_remove_local_pending (list, contact);
+                       g_signal_emit_by_name (list, "contact-removed", contact);
+               }
                g_object_unref (contact);
        }
 
@@ -1171,30 +1186,41 @@ tp_contact_list_contact_removed_cb (GossipTelepathyGroup *group,
 }
 
 static void
-tp_contact_list_local_pending_cb (GossipTelepathyGroup *group,
-                                 GArray               *handles,
-                                 guint                 actor_handle,
-                                 guint                 reason,
-                                 const gchar          *message,
-                                 EmpathyTpContactList *list)
+tp_contact_list_pending_cb (GossipTelepathyGroup *group,
+                           GArray               *handles,
+                           guint                 actor_handle,
+                           guint                 reason,
+                           const gchar          *message,
+                           EmpathyTpContactList *list)
 {
        EmpathyTpContactListPriv *priv;
        GList                    *pending_list, *l;
+       TpContactListType         list_type;
 
        priv = GET_PRIV (list);
 
-       pending_list = empathy_tp_contact_list_get_from_handles (list, handles);
+       list_type = tp_contact_list_get_type (list, group);
 
+       pending_list = empathy_tp_contact_list_get_from_handles (list, handles);
        for (l = pending_list; l; l = l->next) {
                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);
+               gossip_debug (DEBUG_DOMAIN, "Contact '%s' pending in list type %d",
+                             gossip_contact_get_name (contact),
+                             list_type);
+
+               if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) {
+                       EmpathyContactListInfo *info;
+
+                       info = empathy_contact_list_info_new (contact, message);
+                       priv->local_pending = g_list_prepend (priv->local_pending,
+                                                             info);
+
+                       g_signal_emit_by_name (list, "local-pending",
+                                              contact, message);
+               }
 
                g_object_unref (contact);
        }
@@ -1202,6 +1228,30 @@ tp_contact_list_local_pending_cb (GossipTelepathyGroup *group,
        g_list_free (pending_list);
 }
 
+static void
+tp_contact_list_remove_local_pending (EmpathyTpContactList *list,
+                                     GossipContact        *contact)
+{
+       EmpathyTpContactListPriv *priv;
+       GList                    *l;
+
+       priv = GET_PRIV (list);
+
+       for (l = priv->local_pending; l; l = l->next) {
+               EmpathyContactListInfo *info;
+
+               info = l->data;
+               if (gossip_contact_equal (contact, info->contact)) {
+                       gossip_debug (DEBUG_DOMAIN, "Contact no more local-pending: %s",
+                                     gossip_contact_get_name (contact));
+
+                       priv->local_pending = g_list_delete_link (priv->local_pending, l);
+                       empathy_contact_list_info_free (info);
+                       break;
+               }
+       }
+}
+
 static void
 tp_contact_list_groups_updated_cb (GossipContact        *contact,
                                   GParamSpec           *param,
@@ -1531,11 +1581,16 @@ tp_contact_list_group_members_removed_cb (GossipTelepathyGroup *group,
 }
 
 static void
-tp_contact_list_get_contacts_foreach (guint           handle,
+tp_contact_list_get_members_foreach (guint           handle,
                                      GossipContact  *contact,
                                      GList         **contacts)
 {
-       *contacts = g_list_append (*contacts, g_object_ref (contact));
+       GossipSubscription subscription;
+
+       subscription = gossip_contact_get_subscription (contact);
+       if (subscription & GOSSIP_SUBSCRIPTION_TO) {
+               *contacts = g_list_append (*contacts, g_object_ref (contact));
+       }
 }
 
 static void