]> git.0d.be Git - empathy.git/blobdiff - src/empathy-event-manager.c
event_manager_approve_channel_cb: use TpChannel API instead of TpGroup
[empathy.git] / src / empathy-event-manager.c
index 4b5cfc5051f2d610f8f6a6a1bd722c99ad60871d..594bde8b6e07ee391894f29072eb29c47e1864e5 100644 (file)
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2007-2008 Collabora Ltd.
  *
@@ -17,6 +16,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  * 
  * Authors: Xavier Claessens <xclaesse@gmail.com>
+ *          Sjoerd Simons <sjoerd.simons@collabora.co.uk>
  */
 
 #include <config.h>
 #include <libempathy/empathy-contact-factory.h>
 #include <libempathy/empathy-contact-manager.h>
 #include <libempathy/empathy-tp-chat.h>
+#include <libempathy/empathy-tp-call.h>
 #include <libempathy/empathy-tp-group.h>
 #include <libempathy/empathy-utils.h>
+#include <libempathy/empathy-call-factory.h>
 
 #include <extensions/extensions.h>
 
 #include <libempathy-gtk/empathy-images.h>
 #include <libempathy-gtk/empathy-contact-dialogs.h>
+#include <libempathy-gtk/empathy-ui-utils.h>
 
 #include "empathy-event-manager.h"
+#include "empathy-main-window.h"
+#include "empathy-tube-dispatch.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
 #include <libempathy/empathy-debug.h>
 
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyEventManager)
+
+typedef struct {
+  EmpathyEventManager *manager;
+  EmpathyDispatchOperation *operation;
+  gulong approved_handler;
+  gulong claimed_handler;
+  gulong invalidated_handler;
+  /* Remove contact if applicable */
+  EmpathyContact *contact;
+  /* Tube dispatcher if applicable */
+  EmpathyTubeDispatch *tube_dispatch;
+  /* option signal handler */
+  gulong handler;
+  /* optional accept widget */
+  GtkWidget *dialog;
+} EventManagerApproval;
+
 typedef struct {
-       EmpathyDispatcher     *dispatcher;
-       EmpathyContactManager *contact_manager;
-       GSList                *events;
+  EmpathyDispatcher *dispatcher;
+  EmpathyContactManager *contact_manager;
+  GSList *events;
+  /* Ongoing approvals */
+  GSList *approvals;
+
+  /* voip ringing sound */
+  guint voip_timeout;
+  gint ringing;
 } EmpathyEventManagerPriv;
 
 typedef struct _EventPriv EventPriv;
 typedef void (*EventFunc) (EventPriv *event);
 
 struct _EventPriv {
-       EmpathyEvent         public;
-       TpChannel           *channel;
-       EmpathyEventManager *manager;
-       EventFunc            func;
-       gpointer             user_data;
+  EmpathyEvent public;
+  EmpathyEventManager *manager;
+  EventManagerApproval *approval;
+  EventFunc func;
+  gboolean inhibit;
+  gpointer user_data;
 };
 
 enum {
-       EVENT_ADDED,
-       EVENT_REMOVED,
-       LAST_SIGNAL
+  EVENT_ADDED,
+  EVENT_REMOVED,
+  EVENT_UPDATED,
+  LAST_SIGNAL
 };
 
 static guint signals[LAST_SIGNAL];
 
 G_DEFINE_TYPE (EmpathyEventManager, empathy_event_manager, G_TYPE_OBJECT);
 
+static EmpathyEventManager * manager_singleton = NULL;
+
+static EventManagerApproval *
+event_manager_approval_new (EmpathyEventManager *manager,
+  EmpathyDispatchOperation *operation)
+{
+  EventManagerApproval *result = g_slice_new0 (EventManagerApproval);
+  result->operation = g_object_ref (operation);
+  result->manager = manager;
+
+  return result;
+}
+
+static void
+event_manager_approval_free (EventManagerApproval *approval)
+{
+  g_signal_handler_disconnect (approval->operation,
+    approval->approved_handler);
+  g_signal_handler_disconnect (approval->operation,
+    approval->claimed_handler);
+  g_signal_handler_disconnect (approval->operation,
+    approval->invalidated_handler);
+  g_object_unref (approval->operation);
+
+  if (approval->contact != NULL)
+    g_object_unref (approval->contact);
+
+  if (approval->tube_dispatch != NULL)
+    g_object_unref (approval->tube_dispatch);
+
+  if (approval->dialog != NULL)
+    {
+      gtk_widget_destroy (approval->dialog);
+    }
+
+  g_slice_free (EventManagerApproval, approval);
+}
+
 static void event_remove (EventPriv *event);
 
 static void
 event_free (EventPriv *event)
 {
-       g_free (event->public.icon_name);
-       g_free (event->public.message);
+  g_free (event->public.icon_name);
+  g_free (event->public.header);
+  g_free (event->public.message);
 
-       if (event->public.contact) {
-               g_object_unref (event->public.contact);
-       }
+  if (event->public.contact)
+    {
+      g_object_unref (event->public.contact);
+    }
 
-       if (event->channel) {
-               g_signal_handlers_disconnect_by_func (event->channel,
-                                                     event_remove,
-                                                     event);
-               g_object_unref (event->channel);
-       }
-       g_slice_free (EventPriv, event);
+  g_slice_free (EventPriv, event);
+}
+
+static void event_manager_ringing_finished_cb (ca_context *c, guint id,
+  int error_code, gpointer user_data);
+
+static gboolean
+event_manager_ringing_timeout_cb (gpointer data)
+{
+  EmpathyEventManager *manager = EMPATHY_EVENT_MANAGER (data);
+  EmpathyEventManagerPriv *priv = GET_PRIV (manager);
+
+  priv->voip_timeout = 0;
+
+  empathy_sound_play_full (empathy_main_window_get (),
+      EMPATHY_SOUND_PHONE_INCOMING, event_manager_ringing_finished_cb,
+      manager);
+
+  return FALSE;
+}
+
+static gboolean
+event_manager_ringing_idle_cb (gpointer data)
+{
+  EmpathyEventManager *manager = EMPATHY_EVENT_MANAGER (data);
+  EmpathyEventManagerPriv *priv = GET_PRIV (manager);
+
+  if (priv->ringing > 0)
+    priv->voip_timeout = g_timeout_add (500, event_manager_ringing_timeout_cb,
+      data);
+
+  return FALSE;
 }
 
 static void
-event_remove (EventPriv *event)
+event_manager_ringing_finished_cb (ca_context *c, guint id, int error_code,
+  gpointer user_data)
 {
-       EmpathyEventManagerPriv *priv = GET_PRIV (event->manager);
+  if (error_code == CA_ERROR_CANCELED)
+    return;
 
-       DEBUG ("Removing event %p", event);
-       priv->events = g_slist_remove (priv->events, event);
-       g_signal_emit (event->manager, signals[EVENT_REMOVED], 0, event);
-       event_free (event);
+  g_idle_add (event_manager_ringing_idle_cb, user_data);
 }
 
 static void
-event_manager_add (EmpathyEventManager *manager,
-                  EmpathyContact      *contact,
-                  const gchar         *icon_name,
-                  const gchar         *message,
-                  TpChannel           *channel,
-                  EventFunc            func,
-                  gpointer             user_data)
+event_manager_start_ringing (EmpathyEventManager *manager)
 {
-       EmpathyEventManagerPriv *priv = GET_PRIV (manager);
-       EventPriv               *event;
+  EmpathyEventManagerPriv *priv = GET_PRIV (manager);
 
-       event = g_slice_new0 (EventPriv);
-       event->public.contact = contact ? g_object_ref (contact) : NULL;
-       event->public.icon_name = g_strdup (icon_name);
-       event->public.message = g_strdup (message);
-       event->manager = manager;
-       event->func = func;
-       event->user_data = user_data;
+  priv->ringing++;
 
-       if (channel) {
-               event->channel = g_object_ref (channel);
-               g_signal_connect_swapped (channel, "invalidated",
-                                         G_CALLBACK (event_remove),
-                                         event);
-       }
+  if (priv->ringing == 1)
+    {
+      empathy_sound_play_full (empathy_main_window_get (),
+        EMPATHY_SOUND_PHONE_INCOMING, event_manager_ringing_finished_cb,
+        manager);
+    }
+}
+
+static void
+event_manager_stop_ringing (EmpathyEventManager *manager)
+{
+  EmpathyEventManagerPriv *priv = GET_PRIV (manager);
+
+  priv->ringing--;
+
+  if (priv->ringing > 0)
+    return;
+
+  empathy_sound_stop (EMPATHY_SOUND_PHONE_INCOMING);
+
+  if (priv->voip_timeout != 0)
+    {
+      g_source_remove (priv->voip_timeout);
+      priv->voip_timeout = 0;
+    }
+}
 
-       DEBUG ("Adding event %p", event);
-       priv->events = g_slist_prepend (priv->events, event);
-       g_signal_emit (event->manager, signals[EVENT_ADDED], 0, event);
+static void
+event_remove (EventPriv *event)
+{
+  EmpathyEventManagerPriv *priv = GET_PRIV (event->manager);
+
+  DEBUG ("Removing event %p", event);
+  priv->events = g_slist_remove (priv->events, event);
+  g_signal_emit (event->manager, signals[EVENT_REMOVED], 0, event);
+  event_free (event);
+}
+
+static void
+event_manager_add (EmpathyEventManager *manager, EmpathyContact *contact,
+  const gchar *icon_name, const gchar *header, const gchar *message,
+  EventManagerApproval *approval, EventFunc func, gpointer user_data)
+{
+  EmpathyEventManagerPriv *priv = GET_PRIV (manager);
+  EventPriv               *event;
+
+  event = g_slice_new0 (EventPriv);
+  event->public.contact = contact ? g_object_ref (contact) : NULL;
+  event->public.icon_name = g_strdup (icon_name);
+  event->public.header = g_strdup (header);
+  event->public.message = g_strdup (message);
+  event->inhibit = FALSE;
+  event->func = func;
+  event->user_data = user_data;
+  event->manager = manager;
+  event->approval = approval;
+
+  DEBUG ("Adding event %p", event);
+  priv->events = g_slist_prepend (priv->events, event);
+  g_signal_emit (event->manager, signals[EVENT_ADDED], 0, event);
 }
 
 static void
 event_channel_process_func (EventPriv *event)
 {
-       EmpathyEventManagerPriv *priv = GET_PRIV (event->manager);
+  empathy_dispatch_operation_approve (event->approval->operation);
+}
 
-       /* This will emit "dispatch-channel" and the event will be removed
-        * in the callback of that signal, no need to remove the event here. */ 
-       empathy_dispatcher_channel_process (priv->dispatcher, event->channel);
+static void
+event_text_channel_process_func (EventPriv *event)
+{
+  EmpathyTpChat *tp_chat;
+
+  if (event->approval->handler != 0)
+    {
+      tp_chat = EMPATHY_TP_CHAT
+        (empathy_dispatch_operation_get_channel_wrapper (event->approval->operation));
+  
+      g_signal_handler_disconnect (tp_chat, event->approval->handler);
+      event->approval->handler = 0;
+    }
+
+  empathy_dispatch_operation_approve (event->approval->operation);
 }
 
-static gboolean
-event_manager_chat_unref_idle (gpointer user_data)
+static EventPriv *
+event_lookup_by_approval (EmpathyEventManager *manager,
+  EventManagerApproval *approval)
 {
-       g_object_unref (user_data);
-       return FALSE;
+  EmpathyEventManagerPriv *priv = GET_PRIV (manager);
+  GSList *l;
+  EventPriv *retval = NULL;
+
+  for (l = priv->events; l; l = l->next)
+    {
+      EventPriv *event = l->data;
+
+      if (event->approval == approval)
+        {
+          retval = event;
+          break;
+        }
+    }
+
+  return retval;
 }
 
 static void
-event_manager_chat_message_received_cb (EmpathyTpChat       *tp_chat,
-                                       EmpathyMessage      *message,
-                                       EmpathyEventManager *manager)
+event_update (EmpathyEventManager *manager, EventPriv *event,
+  const char *icon_name, const char *header, const char *msg)
 {
-       EmpathyContact  *sender;
-       gchar           *msg;
-       TpChannel       *channel;
+  g_free (event->public.icon_name);
+  g_free (event->public.header);
+  g_free (event->public.message);
 
-       g_idle_add (event_manager_chat_unref_idle, tp_chat);
-       g_signal_handlers_disconnect_by_func (tp_chat,
-                                             event_manager_chat_message_received_cb,
-                                             manager);
+  event->public.icon_name = g_strdup (icon_name);
+  event->public.header = g_strdup (header);
+  event->public.message = g_strdup (msg);
 
-       sender = empathy_message_get_sender (message);
-       msg = g_strdup_printf (_("New message from %s:\n%s"),
-                              empathy_contact_get_name (sender),
-                              empathy_message_get_body (message));
+  g_signal_emit (manager, signals[EVENT_UPDATED], 0, event);
+}
 
-       channel = empathy_tp_chat_get_channel (tp_chat);
-       event_manager_add (manager, sender, EMPATHY_IMAGE_NEW_MESSAGE, msg,
-                          channel, event_channel_process_func, NULL);
+static void
+event_manager_call_window_confirmation_dialog_response_cb (GtkDialog *dialog,
+  gint response, gpointer user_data)
+{
+  EventManagerApproval *approval = user_data;
+
+  gtk_widget_destroy (approval->dialog);
+  approval->dialog = NULL;
+
+  if (response != GTK_RESPONSE_ACCEPT)
+    {
+      EmpathyTpCall *call =
+        EMPATHY_TP_CALL (
+          empathy_dispatch_operation_get_channel_wrapper (
+            approval->operation));
+
+      g_object_ref (call);
+      if (empathy_dispatch_operation_claim (approval->operation))
+        empathy_tp_call_close (call);
+      g_object_unref (call);
+
+    }
+  else
+    {
+      EmpathyCallFactory *factory = empathy_call_factory_get ();
+      empathy_call_factory_claim_channel (factory, approval->operation);
+    }
+}
 
-       g_free (msg);
+static void
+event_channel_process_voip_func (EventPriv *event)
+{
+  GtkWidget *dialog;
+  GtkWidget *button;
+  GtkWidget *image;
+
+  if (event->approval->dialog != NULL)
+    {
+      gtk_window_present (GTK_WINDOW (event->approval->dialog));
+      return;
+    }
+
+  dialog = gtk_message_dialog_new (GTK_WINDOW (empathy_main_window_get()),
+      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+      GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Incoming call"));
+  gtk_message_dialog_format_secondary_text (
+    GTK_MESSAGE_DIALOG (dialog),
+      _("%s is calling you, do you want to answer?"),
+      empathy_contact_get_name (event->approval->contact));
+
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+      GTK_RESPONSE_OK);
+
+  button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+      _("_Reject"), GTK_RESPONSE_REJECT);
+  image = gtk_image_new_from_icon_name (GTK_STOCK_CANCEL,
+    GTK_ICON_SIZE_BUTTON);
+  gtk_button_set_image (GTK_BUTTON (button), image);
+
+  button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+      _("_Answer"), GTK_RESPONSE_ACCEPT);
+
+  image = gtk_image_new_from_icon_name (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
+  gtk_button_set_image (GTK_BUTTON (button), image);
+
+  g_signal_connect (dialog, "response",
+      G_CALLBACK (event_manager_call_window_confirmation_dialog_response_cb),
+      event->approval);
+
+  gtk_widget_show (dialog);
+
+  event->approval->dialog = dialog;
 }
 
 static void
-event_manager_filter_channel_cb (EmpathyDispatcher   *dispatcher,
-                                TpChannel           *channel,
-                                EmpathyEventManager *manager)
+event_manager_chat_message_received_cb (EmpathyTpChat *tp_chat,
+  EmpathyMessage *message, EventManagerApproval *approval)
 {
-       gchar *channel_type;
+  EmpathyContact  *sender;
+  gchar           *header;
+  const gchar     *msg;
+  TpChannel       *channel;
+  EventPriv       *event;
+
+  /* try to update the event if it's referring to a chat which is already in the
+   * queue. */
+  event = event_lookup_by_approval (approval->manager, approval);
+
+  if (event != NULL && event->inhibit && approval->handler != 0)
+    {
+      g_signal_handler_disconnect (tp_chat, approval->handler);
+      approval->handler = 0;
+      return;
+    }
+
+  sender = empathy_message_get_sender (message);
+  header = g_strdup_printf (_("New message from %s"),
+                            empathy_contact_get_name (sender));
+  msg = empathy_message_get_body (message);
+
+  channel = empathy_tp_chat_get_channel (tp_chat);
+
+  if (event != NULL)
+    event_update (approval->manager, event, EMPATHY_IMAGE_NEW_MESSAGE, header, msg);
+  else
+    event_manager_add (approval->manager, sender, EMPATHY_IMAGE_NEW_MESSAGE, header,
+      msg, approval, event_text_channel_process_func, NULL);
+
+  g_free (header);
+  empathy_sound_play (empathy_main_window_get (),
+    EMPATHY_SOUND_CONVERSATION_NEW);
+}
 
-       g_object_get (channel, "channel-type", &channel_type, NULL);
-       if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)) {
-               EmpathyTpChat *tp_chat;
+static void
+event_manager_approval_done (EventManagerApproval *approval)
+{
+  EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
+  GSList                  *l;
+
+  if (approval->operation != NULL)
+    {
+      GQuark channel_type;
+
+      channel_type = empathy_dispatch_operation_get_channel_type_id (
+          approval->operation);
+      if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
+        {
+          event_manager_stop_ringing (approval->manager);
+        }
+    }
+
+  priv->approvals = g_slist_remove (priv->approvals, approval);
+
+  for (l = priv->events; l; l = l->next)
+    {
+      EventPriv *event = l->data;
+
+      if (event->approval == approval)
+        {
+          event_remove (event);
+          break;
+        }
+    }
+
+  event_manager_approval_free (approval);
+}
 
-               tp_chat = empathy_tp_chat_new (channel);
-               g_signal_connect (tp_chat, "message-received",
-                                 G_CALLBACK (event_manager_chat_message_received_cb),
-                                 manager);
-       }
-       else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) {
-               EmpathyTpGroup *tp_group;
-               EmpathyContact *contact;
-               gchar          *msg;
-
-               tp_group = empathy_tp_group_new (channel);
-               empathy_run_until_ready (tp_group);
-               empathy_tp_group_get_invitation (tp_group, &contact);
-               empathy_contact_run_until_ready (contact,
-                                                EMPATHY_CONTACT_READY_NAME,
-                                                NULL);
-
-               msg = g_strdup_printf (_("Incoming call from %s"),
-                                      empathy_contact_get_name (contact));
-
-               event_manager_add (manager, contact, EMPATHY_IMAGE_VOIP, msg,
-                                  channel, event_channel_process_func, NULL);
-
-               g_free (msg);
-               g_object_unref (contact);
-               g_object_unref (tp_group);
-       }
-       else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_FILE_TRANSFER)) {
-               EmpathyContact        *contact;
-               gchar                 *msg;
-               TpHandle               handle;
-               McAccount             *account;
-               EmpathyContactFactory *factory;
-
-               factory = empathy_contact_factory_new ();
-               handle = tp_channel_get_handle (channel, NULL);
-               account = empathy_channel_get_account (channel);
-
-               contact = empathy_contact_factory_get_from_handle (factory,
-                                                                  account,
-                                                                  handle);
-
-               empathy_contact_run_until_ready (contact,
-                       EMPATHY_CONTACT_READY_NAME, NULL);
-
-               msg = g_strdup_printf (_("Incoming file transfer from %s"),
-                                      empathy_contact_get_name (contact));
-
-               event_manager_add (manager, contact,
-                                  EMPATHY_IMAGE_DOCUMENT_SEND,
-                                  msg, channel,
-                                  event_channel_process_func, NULL);
-
-               g_object_unref (factory);
-               g_object_unref (account);
-       }
+static void
+event_manager_operation_approved_cb (EmpathyDispatchOperation *operation,
+  EventManagerApproval *approval)
+{
+  event_manager_approval_done (approval);
+}
 
-       g_free (channel_type);
+static void
+event_manager_operation_claimed_cb (EmpathyDispatchOperation *operation,
+  EventManagerApproval *approval)
+{
+  event_manager_approval_done (approval);
 }
 
 static void
-event_manager_dispatch_channel_cb (EmpathyDispatcher   *dispatcher,
-                                  TpChannel           *channel,
-                                  EmpathyEventManager *manager)
+event_manager_operation_invalidated_cb (EmpathyDispatchOperation *operation,
+  guint domain, gint code, gchar *message,
+  EventManagerApproval *approval)
 {
-       EmpathyEventManagerPriv *priv = GET_PRIV (manager);
-       GSList                  *l;
+  event_manager_approval_done (approval);
+}
 
-       for (l = priv->events; l; l = l->next) {
-               EventPriv *event = l->data;
+static void
+event_manager_media_channel_got_name_cb (EmpathyContact *contact,
+  const GError *error, gpointer user_data, GObject *object)
+{
+  EventManagerApproval *approval = user_data;
+  gchar *header;
 
-               if (event->channel &&
-                   empathy_proxy_equal (channel, event->channel)) {
-                       event_remove (event);
-                       break;
-               }
-       }
+  if (error != NULL)
+    {
+      /* FIXME just returning assuming the operation will be invalidated as
+       * well */
+      return;
+    }
+
+  header = g_strdup_printf (_("Incoming call from %s"),
+    empathy_contact_get_name (contact));
+
+  event_manager_add (approval->manager,
+    approval->contact, EMPATHY_IMAGE_VOIP, header, NULL,
+    approval, event_channel_process_voip_func, NULL);
+
+  g_free (header);
+  event_manager_start_ringing (approval->manager);
+}
+
+static void
+event_manager_media_channel_got_contact (EventManagerApproval *approval)
+{
+  empathy_contact_call_when_ready (approval->contact,
+     EMPATHY_CONTACT_READY_NAME, event_manager_media_channel_got_name_cb,
+        approval, NULL, G_OBJECT (approval->manager));
 }
 
-#define TUBE_NO_APP_MESSAGE _("%s is offering you an invitation, but " \
-                             "you don't have the needed external " \
-                             "application to handle it.")
+static void
+event_manager_media_channel_contact_changed_cb (EmpathyTpCall *call,
+  GParamSpec *param, EventManagerApproval *approval)
+{
+  EmpathyContact *contact;
+
+  g_object_get (G_OBJECT (call), "contact", &contact, NULL);
+
+  if (contact == NULL)
+    return;
+
+  approval->contact = contact;
+  event_manager_media_channel_got_contact (approval);
+}
 
 static void
-event_tube_process_func (EventPriv *event)
+event_manager_tube_approved_cb (EventPriv *event)
 {
-       EmpathyEventManagerPriv *priv = GET_PRIV (event->manager);
-       EmpathyDispatcherTube   *tube = (EmpathyDispatcherTube*) event->user_data;
+  empathy_tube_dispatch_handle (event->approval->tube_dispatch);
+}
 
-       if (tube->activatable) {
-               empathy_dispatcher_tube_process (priv->dispatcher, tube);
-       } else {
-               GtkWidget *dialog;
-               gchar     *str;
-
-               /* Tell the user that the tube can't be handled */
-               str = g_strdup_printf (TUBE_NO_APP_MESSAGE,
-                                      empathy_contact_get_name (tube->initiator));
-
-               dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
-                                                GTK_MESSAGE_ERROR,
-                                                GTK_BUTTONS_OK,
-                                                "%s", str);
-               gtk_window_set_title (GTK_WINDOW (dialog),
-                                     _("Invitation Error"));
-               g_free (str);
-
-               gtk_widget_show (dialog);
-               g_signal_connect (dialog, "response",
-                                 G_CALLBACK (gtk_widget_destroy),
-                                 NULL);
-       }
+static void
+event_manager_add_tube_approval (EventManagerApproval *approval,
+  EmpathyTubeDispatchAbility ability)
+{
+  const gchar *icon_name;
+  gchar       *header;
+  const gchar *msg;
+
+  header = g_strdup_printf (_("%s is offering you an invitation"),
+    empathy_contact_get_name (approval->contact));
+
+  if (ability == EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE)
+    {
+      icon_name = GTK_STOCK_EXECUTE;
+      msg = _("An external application will be started to handle it.");
+    }
+  else
+    {
+      icon_name = GTK_STOCK_DIALOG_ERROR;
+      msg = _("You don't have the needed external "
+              "application to handle it.");
+    }
+
+  event_manager_add (approval->manager, approval->contact, icon_name, header,
+    msg, approval, event_manager_tube_approved_cb, approval);
+
+  g_free (header);
+  /* FIXME better sound for incoming tubes ? */
+  empathy_sound_play (empathy_main_window_get (),
+    EMPATHY_SOUND_CONVERSATION_NEW);
+}
 
-       empathy_dispatcher_tube_unref (tube);
-       event_remove (event);
+static void
+event_manager_tube_dispatch_ability_cb (GObject *object,
+   GParamSpec *spec, gpointer user_data)
+{
+  EventManagerApproval *approval = (EventManagerApproval *)user_data;
+  EmpathyTubeDispatchAbility dispatchability;
+
+  dispatchability =
+    empathy_tube_dispatch_is_dispatchable (approval->tube_dispatch);
+
+  if (dispatchability != EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN)
+    {
+      event_manager_add_tube_approval (approval, dispatchability);
+      g_signal_handler_disconnect (object, approval->handler);
+      approval->handler = 0;
+    }
 }
 
 static void
-event_manager_filter_tube_cb (EmpathyDispatcher     *dispatcher,
-                             EmpathyDispatcherTube *tube,
-                             EmpathyEventManager   *manager)
+event_manager_tube_got_contact_name_cb (EmpathyContact *contact,
+  const GError *error, gpointer user_data, GObject *object)
 {
-       const gchar *icon_name;
-       gchar       *msg;
+  EventManagerApproval *approval = (EventManagerApproval *)user_data;
+  EmpathyTubeDispatchAbility dispatchability;
+
+  if (error != NULL)
+    {
+      /* FIXME?, we assume that the operation gets invalidated as well (if it
+       * didn't already */
+       return;
+    }
+
+  dispatchability = empathy_tube_dispatch_is_dispatchable
+    (approval->tube_dispatch);
+
+
+  switch (dispatchability)
+    {
+      case EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN:
+        approval->handler = g_signal_connect (approval->tube_dispatch,
+          "notify::dispatchability",
+          G_CALLBACK (event_manager_tube_dispatch_ability_cb), approval);
+        break;
+      case EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE:
+        /* fallthrough */
+      case EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE:
+        event_manager_add_tube_approval (approval, dispatchability);
+        break;
+    }
+}
 
-       empathy_contact_run_until_ready (tube->initiator,
-                                        EMPATHY_CONTACT_READY_NAME, NULL);
+static void
+invite_dialog_response_cb (GtkDialog *dialog,
+                           gint response,
+                           EventManagerApproval *approval)
+{
+  EmpathyTpChat *tp_chat;
+  TpChannel *channel;
+  EmpathyTpGroup *group;
+  EmpathyContact *self_contact;
 
-       if (tube->activatable) {
-               icon_name = GTK_STOCK_EXECUTE;
-               msg = g_strdup_printf (_("%s is offering you an invitation. An external "
-                                        "application will be started to handle it."),
-                                      empathy_contact_get_name (tube->initiator));
-       } else {
-               icon_name = GTK_STOCK_DIALOG_ERROR;
-               msg = g_strdup_printf (TUBE_NO_APP_MESSAGE,
-                                      empathy_contact_get_name (tube->initiator));
-       }
+  gtk_widget_destroy (GTK_WIDGET (approval->dialog));
+  approval->dialog = NULL;
+
+  tp_chat = EMPATHY_TP_CHAT (empathy_dispatch_operation_get_channel_wrapper (
+        approval->operation));
+
+  if (response != GTK_RESPONSE_OK)
+    {
+      /* close channel */
+      DEBUG ("Muc invitation rejected");
+
+      if (empathy_dispatch_operation_claim (approval->operation))
+        empathy_tp_chat_close (tp_chat);
+      empathy_tp_chat_close (tp_chat);
+      return;
+    }
+
+  DEBUG ("Muc invitation accepted");
+
+  /* join the room */
+  channel = empathy_tp_chat_get_channel (tp_chat);
+
+  group = empathy_tp_group_new (channel);
+  empathy_run_until_ready (group);
+
+  self_contact = empathy_tp_group_get_self_contact (group);
+  empathy_tp_group_add_member (group, self_contact, NULL);
+
+  empathy_dispatch_operation_approve (approval->operation);
+
+  g_object_unref (group);
+  g_object_unref (self_contact);
+}
+
+static void
+event_room_channel_process_func (EventPriv *event)
+{
+  GtkWidget *dialog, *button, *image;
+  TpChannel *channel = empathy_dispatch_operation_get_channel (
+      event->approval->operation);
+
+  if (event->approval->dialog != NULL)
+    {
+      gtk_window_present (GTK_WINDOW (event->approval->dialog));
+      return;
+    }
+
+  /* create dialog */
+  dialog = gtk_message_dialog_new (NULL, 0,
+      GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Room invitation"));
+
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+      _("%s is inviting you to join %s"),
+      empathy_contact_get_name (event->approval->contact),
+      tp_channel_get_identifier (channel));
+
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+      GTK_RESPONSE_OK);
+
+  button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+      _("_Decline"), GTK_RESPONSE_CANCEL);
+  image = gtk_image_new_from_icon_name (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
+  gtk_button_set_image (GTK_BUTTON (button), image);
 
-       event_manager_add (manager, tube->initiator, icon_name, msg,
-                          tube->channel, event_tube_process_func,
-                          empathy_dispatcher_tube_ref (tube));
+  button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+      _("_Join"), GTK_RESPONSE_OK);
+  image = gtk_image_new_from_icon_name (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
+  gtk_button_set_image (GTK_BUTTON (button), image);
 
-       g_free (msg);
+  g_signal_connect (dialog, "response",
+      G_CALLBACK (invite_dialog_response_cb), event->approval);
+
+  gtk_widget_show (dialog);
+
+  event->approval->dialog = dialog;
+}
+
+static void
+event_manager_approve_channel_cb (EmpathyDispatcher *dispatcher,
+  EmpathyDispatchOperation  *operation, EmpathyEventManager *manager)
+{
+  const gchar *channel_type;
+  EventManagerApproval *approval;
+  EmpathyEventManagerPriv *priv = GET_PRIV (manager);
+
+  channel_type = empathy_dispatch_operation_get_channel_type (operation);
+
+  approval = event_manager_approval_new (manager, operation);
+  priv->approvals = g_slist_prepend (priv->approvals, approval);
+
+  approval->approved_handler = g_signal_connect (operation, "approved",
+    G_CALLBACK (event_manager_operation_approved_cb), approval);
+
+  approval->claimed_handler = g_signal_connect (operation, "claimed",
+     G_CALLBACK (event_manager_operation_claimed_cb), approval);
+
+  approval->invalidated_handler = g_signal_connect (operation, "invalidated",
+     G_CALLBACK (event_manager_operation_invalidated_cb), approval);
+
+  if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT))
+    {
+      EmpathyTpChat *tp_chat =
+        EMPATHY_TP_CHAT (
+          empathy_dispatch_operation_get_channel_wrapper (operation));
+      TpChannel *channel = empathy_tp_chat_get_channel (tp_chat);
+      TpHandle handle;
+      TpHandleType handle_type;
+
+      handle = tp_channel_get_handle (channel, &handle_type);
+
+      if (handle_type == TP_HANDLE_TYPE_CONTACT)
+        {
+          /* 1-1 text channel, wait for the first message */
+          approval->handler = g_signal_connect (tp_chat, "message-received",
+            G_CALLBACK (event_manager_chat_message_received_cb), approval);
+        }
+      else if (handle_type == TP_HANDLE_TYPE_ROOM)
+        {
+          gchar *msg;
+          const gchar *invite_msg;
+          TpHandle self_handle, inviter;
+          EmpathyContactFactory *contact_factory;
+          McAccount *account;
+
+          self_handle = tp_channel_group_get_self_handle (channel);
+
+          if (self_handle == 0 || !tp_channel_group_get_local_pending_info (
+                channel, self_handle, &inviter, NULL, &invite_msg))
+            {
+              DEBUG ("can't handle a incoming muc to which we have not been "
+                  "invited");
+
+              if (empathy_dispatch_operation_claim (approval->operation))
+                empathy_tp_chat_close (tp_chat);
+              return;
+            }
+
+          account = empathy_tp_chat_get_account (tp_chat);
+          contact_factory = empathy_contact_factory_dup_singleton ();
+
+          approval->contact = empathy_contact_factory_get_from_handle (
+              contact_factory, account, inviter);
+
+          /* We are invited to a room */
+          msg = g_strdup_printf ("%s invited you to join %s",
+              empathy_contact_get_name (approval->contact),
+              tp_channel_get_identifier (channel));
+
+          event_manager_add (approval->manager,
+            approval->contact, EMPATHY_IMAGE_GROUP_MESSAGE, msg, invite_msg,
+            approval, event_room_channel_process_func, NULL);
+
+          empathy_sound_play (empathy_main_window_get (),
+            EMPATHY_SOUND_CONVERSATION_NEW);
+
+          g_object_unref (contact_factory);
+        }
+    }
+  else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
+    {
+      EmpathyContact *contact;
+      EmpathyTpCall *call = EMPATHY_TP_CALL (
+          empathy_dispatch_operation_get_channel_wrapper (operation));
+
+      g_object_get (G_OBJECT (call), "contact", &contact, NULL);
+
+      if (contact == NULL)
+        {
+          g_signal_connect (call, "notify::contact",
+            G_CALLBACK (event_manager_media_channel_contact_changed_cb),
+            approval);
+        }
+      else
+        {
+          approval->contact = contact;
+          event_manager_media_channel_got_contact (approval);
+        }
+
+    }
+  else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
+    {
+      EmpathyContact        *contact;
+      gchar                 *header;
+      TpHandle               handle;
+      McAccount             *account;
+      EmpathyContactFactory *factory;
+      TpChannel *channel = empathy_dispatch_operation_get_channel (operation);
+
+      factory = empathy_contact_factory_dup_singleton ();
+      handle = tp_channel_get_handle (channel, NULL);
+      account = empathy_channel_get_account (channel);
+
+      contact = empathy_contact_factory_get_from_handle (factory, account,
+        handle);
+
+      empathy_contact_run_until_ready (contact,
+        EMPATHY_CONTACT_READY_NAME, NULL);
+
+      header = g_strdup_printf (_("Incoming file transfer from %s"),
+        empathy_contact_get_name (contact));
+
+      event_manager_add (manager, contact, EMPATHY_IMAGE_DOCUMENT_SEND,
+        header, NULL, approval, event_channel_process_func, NULL);
+
+      /* FIXME better sound for incoming file transfers ?*/
+      empathy_sound_play (empathy_main_window_get (),
+        EMPATHY_SOUND_CONVERSATION_NEW);
+
+      g_object_unref (factory);
+      g_object_unref (account);
+      g_free (header);
+    }
+  else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_STREAM_TUBE) ||
+      !tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_DBUS_TUBE))
+    {
+      EmpathyContact        *contact;
+      TpHandle               handle;
+      TpHandleType           handle_type;
+      McAccount             *account;
+      EmpathyContactFactory *factory;
+      EmpathyTubeDispatch *tube_dispatch;
+      TpChannel *channel;
+
+      channel = empathy_dispatch_operation_get_channel (operation);
+
+      handle = tp_channel_get_handle (channel, &handle_type);
+
+      /* Only understand p2p tubes */
+      if (handle_type != TP_HANDLE_TYPE_CONTACT)
+        return;
+
+      factory = empathy_contact_factory_dup_singleton ();
+      account = empathy_channel_get_account (channel);
+
+      contact = empathy_contact_factory_get_from_handle (factory, account,
+        handle);
+
+      tube_dispatch = empathy_tube_dispatch_new (operation);
+
+      approval->contact = contact;
+      approval->tube_dispatch = tube_dispatch;
+
+      empathy_contact_call_when_ready (contact,
+        EMPATHY_CONTACT_READY_NAME, event_manager_tube_got_contact_name_cb,
+        approval, NULL, G_OBJECT (manager));
+
+      g_object_unref (factory);
+      g_object_unref (account);
+    }
+  else
+    {
+      DEBUG ("Unknown channel type, ignoring..");
+    }
 }
 
 static void
 event_pending_subscribe_func (EventPriv *event)
 {
-       empathy_subscription_dialog_show (event->public.contact, NULL);
-       event_remove (event);
+  empathy_subscription_dialog_show (event->public.contact, NULL);
+  event_remove (event);
 }
 
 static void
 event_manager_pendings_changed_cb (EmpathyContactList  *list,
-                                  EmpathyContact      *contact,
-                                  EmpathyContact      *actor,
-                                  guint                reason,
-                                  gchar               *message,
-                                  gboolean             is_pending,
-                                  EmpathyEventManager *manager)
-{
-       EmpathyEventManagerPriv *priv = GET_PRIV (manager);
-       GString                 *str;
-
-       if (!is_pending) {
-               GSList *l;
-
-               for (l = priv->events; l; l = l->next) {
-                       EventPriv *event = l->data;
-
-                       if (event->public.contact == contact &&
-                           event->func == event_pending_subscribe_func) {
-                               event_remove (event);
-                               break;
-                       }
-               }
-
-               return;
-       }
+  EmpathyContact *contact, EmpathyContact *actor,
+  guint reason, gchar *message, gboolean is_pending,
+  EmpathyEventManager *manager)
+{
+  EmpathyEventManagerPriv *priv = GET_PRIV (manager);
+  gchar                   *header, *event_msg;
 
-       empathy_contact_run_until_ready (contact,
-                                        EMPATHY_CONTACT_READY_NAME,
-                                        NULL);
+  if (!is_pending)
+    {
+      GSList *l;
 
-       str = g_string_new (NULL);
-       g_string_printf (str, _("Subscription requested by %s"),
-                        empathy_contact_get_name (contact));   
-       if (!G_STR_EMPTY (message)) {
-               g_string_append_printf (str, _("\nMessage: %s"), message);
-       }
+      for (l = priv->events; l; l = l->next)
+        {
+          EventPriv *event = l->data;
+
+      if (event->public.contact == contact &&
+          event->func == event_pending_subscribe_func)
+        {
+          event_remove (event);
+          break;
+        }
+      }
 
-       event_manager_add (manager, contact, GTK_STOCK_DIALOG_QUESTION, str->str,
-                          NULL, event_pending_subscribe_func, NULL);
+      return;
+    }
 
-       g_string_free (str, TRUE);
+  empathy_contact_run_until_ready (contact, EMPATHY_CONTACT_READY_NAME, NULL);
+
+  header = g_strdup_printf (_("Subscription requested by %s"),
+    empathy_contact_get_name (contact));
+
+  if (!EMP_STR_EMPTY (message))
+    event_msg = g_strdup_printf (_("\nMessage: %s"), message);
+  else
+    event_msg = NULL;
+
+  event_manager_add (manager, contact, GTK_STOCK_DIALOG_QUESTION, header,
+    event_msg, NULL, event_pending_subscribe_func, NULL);
+
+  g_free (event_msg);
+  g_free (header);
+}
+
+static GObject *
+event_manager_constructor (GType type,
+                          guint n_props,
+                          GObjectConstructParam *props)
+{
+       GObject *retval;
+
+       if (manager_singleton) {
+               retval = g_object_ref (manager_singleton);
+       } else {
+               retval = G_OBJECT_CLASS (empathy_event_manager_parent_class)->constructor
+                       (type, n_props, props);
+
+               manager_singleton = EMPATHY_EVENT_MANAGER (retval);
+               g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
+       }
+
+       return retval;
 }
 
 static void
 event_manager_finalize (GObject *object)
 {
-       EmpathyEventManagerPriv *priv = GET_PRIV (object);
-
-       g_slist_foreach (priv->events, (GFunc) event_free, NULL);
-       g_slist_free (priv->events);
-       g_object_unref (priv->contact_manager);
-       g_object_unref (priv->dispatcher);
+  EmpathyEventManagerPriv *priv = GET_PRIV (object);
+
+  g_slist_foreach (priv->events, (GFunc) event_free, NULL);
+  g_slist_free (priv->events);
+  g_slist_foreach (priv->approvals, (GFunc) event_manager_approval_free, NULL);
+  g_slist_free (priv->approvals);
+  g_object_unref (priv->contact_manager);
+  g_object_unref (priv->dispatcher);
 }
 
 static void
 empathy_event_manager_class_init (EmpathyEventManagerClass *klass)
 {
-       GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-       object_class->finalize = event_manager_finalize;
-
-       signals[EVENT_ADDED] =
-               g_signal_new ("event-added",
-                             G_TYPE_FROM_CLASS (klass),
-                             G_SIGNAL_RUN_LAST,
-                             0,
-                             NULL, NULL,
-                             g_cclosure_marshal_VOID__POINTER,
-                             G_TYPE_NONE,
-                             1, G_TYPE_POINTER);
-
-       signals[EVENT_REMOVED] =
-               g_signal_new ("event-removed",
-                             G_TYPE_FROM_CLASS (klass),
-                             G_SIGNAL_RUN_LAST,
-                             0,
-                             NULL, NULL,
-                             g_cclosure_marshal_VOID__POINTER,
-                             G_TYPE_NONE,
-                             1, G_TYPE_POINTER);
-
-       g_type_class_add_private (object_class, sizeof (EmpathyEventManagerPriv));
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = event_manager_finalize;
+  object_class->constructor = event_manager_constructor;
+
+  signals[EVENT_ADDED] =
+    g_signal_new ("event-added",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST,
+      0,
+      NULL, NULL,
+      g_cclosure_marshal_VOID__POINTER,
+      G_TYPE_NONE,
+      1, G_TYPE_POINTER);
+
+  signals[EVENT_REMOVED] =
+  g_signal_new ("event-removed",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST,
+      0,
+      NULL, NULL,
+      g_cclosure_marshal_VOID__POINTER,
+      G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+  signals[EVENT_UPDATED] =
+  g_signal_new ("event-updated",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST,
+      0,
+      NULL, NULL,
+      g_cclosure_marshal_VOID__POINTER,
+      G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+
+  g_type_class_add_private (object_class, sizeof (EmpathyEventManagerPriv));
 }
 
 static void
 empathy_event_manager_init (EmpathyEventManager *manager)
 {
-       EmpathyEventManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
-               EMPATHY_TYPE_EVENT_MANAGER, EmpathyEventManagerPriv);
+  EmpathyEventManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
+    EMPATHY_TYPE_EVENT_MANAGER, EmpathyEventManagerPriv);
 
-       manager->priv = priv;
+  manager->priv = priv;
 
-       priv->dispatcher = empathy_dispatcher_new ();
-       priv->contact_manager = empathy_contact_manager_new ();
-       g_signal_connect (priv->dispatcher, "filter-channel",
-                         G_CALLBACK (event_manager_filter_channel_cb),
-                         manager);
-       g_signal_connect (priv->dispatcher, "dispatch-channel",
-                         G_CALLBACK (event_manager_dispatch_channel_cb),
-                         manager);
-       g_signal_connect (priv->dispatcher, "filter-tube",
-                         G_CALLBACK (event_manager_filter_tube_cb),
-                         manager);
-       g_signal_connect (priv->contact_manager, "pendings-changed",
-                         G_CALLBACK (event_manager_pendings_changed_cb),
-                         manager);
+  priv->dispatcher = empathy_dispatcher_dup_singleton ();
+  priv->contact_manager = empathy_contact_manager_dup_singleton ();
+  g_signal_connect (priv->dispatcher, "approve",
+    G_CALLBACK (event_manager_approve_channel_cb), manager);
+  g_signal_connect (priv->contact_manager, "pendings-changed",
+    G_CALLBACK (event_manager_pendings_changed_cb), manager);
 }
 
 EmpathyEventManager *
-empathy_event_manager_new (void)
+empathy_event_manager_dup_singleton (void)
 {
-       static EmpathyEventManager *manager = NULL;
-
-       if (!manager) {
-               manager = g_object_new (EMPATHY_TYPE_EVENT_MANAGER, NULL);
-               g_object_add_weak_pointer (G_OBJECT (manager), (gpointer) &manager);
-       } else {
-               g_object_ref (manager);
-       }
-
-       return manager;
+  return g_object_new (EMPATHY_TYPE_EVENT_MANAGER, NULL);
 }
 
 GSList *
 empathy_event_manager_get_events (EmpathyEventManager *manager)
 {
-       EmpathyEventManagerPriv *priv = GET_PRIV (manager);
+  EmpathyEventManagerPriv *priv = GET_PRIV (manager);
 
-       g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
+  g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
 
-       return priv->events;
+  return priv->events;
 }
 
 EmpathyEvent *
 empathy_event_manager_get_top_event (EmpathyEventManager *manager)
 {
-       EmpathyEventManagerPriv *priv = GET_PRIV (manager);
+  EmpathyEventManagerPriv *priv = GET_PRIV (manager);
 
-       g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
+  g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
 
-       return priv->events ? priv->events->data : NULL;
+  return priv->events ? priv->events->data : NULL;
 }
 
 void
 empathy_event_activate (EmpathyEvent *event_public)
 {
-       EventPriv *event = (EventPriv*) event_public;
+  EventPriv *event = (EventPriv*) event_public;
 
-       g_return_if_fail (event_public != NULL);
+  g_return_if_fail (event_public != NULL);
 
-       if (event->func) {
-               event->func (event);
-       } else {
-               event_remove (event);
-       }
+  if (event->func)
+    event->func (event);
+  else
+    event_remove (event);
+}
+
+void
+empathy_event_inhibit_updates (EmpathyEvent *event_public)
+{
+  EventPriv *event = (EventPriv *) event_public;
+
+  g_return_if_fail (event_public != NULL);
+
+  event->inhibit = TRUE;
 }