]> git.0d.be Git - empathy.git/commitdiff
Move non-gtk parts of EmpathyFilter to EmpathyDispatcher in libempathy, gtk parts...
authorXavier Claessens <xclaesse@src.gnome.org>
Thu, 8 May 2008 17:30:40 +0000 (17:30 +0000)
committerXavier Claessens <xclaesse@src.gnome.org>
Thu, 8 May 2008 17:30:40 +0000 (17:30 +0000)
svn path=/trunk/; revision=1093

configure.ac
libempathy/Makefile.am
libempathy/empathy-debug.c
libempathy/empathy-debug.h
libempathy/empathy-dispatcher.c [new file with mode: 0644]
libempathy/empathy-dispatcher.h [new file with mode: 0644]
src/Makefile.am
src/empathy-filter.c [deleted file]
src/empathy-filter.h [deleted file]
src/empathy-status-icon.c
src/empathy.c

index d460a4771cb9717aa970beea0b545d31b7919c81..80f48caeb304b418429318be868d9de850146e06 100644 (file)
@@ -30,7 +30,7 @@ GTK_REQUIRED=2.12.0
 GCONF_REQUIRED=1.2.0
 LIBGLADE_REQUIRED=2.0.0
 LIBPANELAPPLET_REQUIRED=2.10.0
-TELEPATHY_GLIB_REQUIRED=0.7.6
+TELEPATHY_GLIB_REQUIRED=0.7.7
 MISSION_CONTROL_REQUIRED=4.61
 ENCHANT_REQUIRED=1.2.0
 ISO_CODES_REQUIRED=0.35
index 16696b432b9d21601d7f91ae09639f7fbe154e70..0db883d3d94ce71409433ff24449f192cf69e486 100644 (file)
@@ -41,7 +41,8 @@ libempathy_la_SOURCES =                                       \
        empathy-irc-network-manager.c                   \
        empathy-irc-network.c                           \
        empathy-irc-server.c                            \
-       empathy-tube-handler.c
+       empathy-tube-handler.c                          \
+       empathy-dispatcher.c
 
 # do not distribute generated files
 nodist_libempathy_la_SOURCES =\
@@ -81,7 +82,8 @@ libempathy_headers =                          \
        empathy-irc-network-manager.h           \
        empathy-irc-network.h                   \
        empathy-irc-server.h                    \
-       empathy-tube-handler.h
+       empathy-tube-handler.h                  \
+       empathy-dispatcher.h
 
 libempathy_includedir = $(includedir)/libempathy/
 libempathy_include_HEADERS =                   \
index 16d61c37c4f8dcf8912ad8b09fe4c2477fe51e8c..b0f7d1810add1da68da2bca95be6ba3090cb7222 100644 (file)
@@ -43,7 +43,7 @@ static GDebugKey keys[] = {
   { "Contact", EMPATHY_DEBUG_CONTACT },
   { "Account", EMPATHY_DEBUG_ACCOUNT },
   { "Irc", EMPATHY_DEBUG_IRC },
-  { "Filter", EMPATHY_DEBUG_FILTER },
+  { "Dispatcher", EMPATHY_DEBUG_DISPATCHER },
   { "Other", EMPATHY_DEBUG_OTHER },
   { 0, }
 };
index 5f15b8c1b29f4fdc1475a0f523f5a3d5e95cb795..42403ca15dce0f41229bed72061d54c9ac624477 100644 (file)
@@ -37,7 +37,7 @@ typedef enum
   EMPATHY_DEBUG_CONTACT = 1 << 3,
   EMPATHY_DEBUG_ACCOUNT = 1 << 4,
   EMPATHY_DEBUG_IRC = 1 << 5,
-  EMPATHY_DEBUG_FILTER = 1 << 6,
+  EMPATHY_DEBUG_DISPATCHER = 1 << 6,
   EMPATHY_DEBUG_OTHER = 1 << 7,
 } EmpathyDebugFlags;
 
diff --git a/libempathy/empathy-dispatcher.c b/libempathy/empathy-dispatcher.c
new file mode 100644 (file)
index 0000000..741b01d
--- /dev/null
@@ -0,0 +1,681 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007-2008 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include <telepathy-glib/enums.h>
+#include <telepathy-glib/connection.h>
+#include <telepathy-glib/util.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/proxy-subclass.h>
+
+#include <libmissioncontrol/mission-control.h>
+#include <libmissioncontrol/mc-account.h>
+
+#include <extensions/extensions.h>
+
+#include "empathy-dispatcher.h"
+#include "empathy-utils.h"
+#include "empathy-tube-handler.h"
+#include "empathy-contact-factory.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
+#include <libempathy/empathy-debug.h>
+
+#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyDispatcher)
+typedef struct {
+       GHashTable     *connections;
+       gpointer        token;
+       MissionControl *mc;
+       GHashTable     *tubes;
+} EmpathyDispatcherPriv;
+
+G_DEFINE_TYPE (EmpathyDispatcher, empathy_dispatcher, G_TYPE_OBJECT);
+
+enum {
+       DISPATCH_CHANNEL,
+       FILTER_CHANNEL,
+       FILTER_TUBE,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+static EmpathyDispatcher *dispatcher = NULL;
+
+void
+empathy_dispatcher_channel_process (EmpathyDispatcher *dispatcher,
+                                   TpChannel         *channel)
+{
+       g_signal_emit (dispatcher, signals[DISPATCH_CHANNEL], 0, channel);
+}
+
+typedef struct {
+       EmpathyDispatcherTube  public;
+       EmpathyContactFactory *factory;
+       gchar                 *bus_name;
+       gchar                 *object_path;
+       guint                  ref_count;
+       gboolean               handled;
+} DispatcherTube;
+
+GType
+empathy_dispatcher_tube_get_type (void)
+{
+       static GType type_id = 0;
+
+       if (!type_id) {
+               type_id = g_boxed_type_register_static ("EmpathyDispatcherTube",
+                                                       (GBoxedCopyFunc) empathy_dispatcher_tube_ref,
+                                                       (GBoxedFreeFunc) empathy_dispatcher_tube_unref);
+       }
+
+       return type_id;
+}
+
+EmpathyDispatcherTube *
+empathy_dispatcher_tube_ref (EmpathyDispatcherTube *data)
+{
+       DispatcherTube *tube = (DispatcherTube*) data;
+
+       g_return_val_if_fail (tube != NULL, NULL);
+
+       tube->ref_count++;
+
+       return data;
+}
+
+void
+empathy_dispatcher_tube_unref (EmpathyDispatcherTube *data)
+{
+       DispatcherTube *tube = (DispatcherTube*) data;
+
+       g_return_if_fail (tube != NULL);
+
+       if (--tube->ref_count == 0) {
+               if (!tube->handled) {
+                       DEBUG ("Tube can't be handled, closing");
+                       tp_cli_channel_type_tubes_call_close_tube (tube->public.channel, -1,
+                                                                  tube->public.id,
+                                                                  NULL, NULL, NULL,
+                                                                  NULL);
+               }
+
+               g_free (tube->bus_name);
+               g_free (tube->object_path);
+               g_object_unref (tube->factory);
+               g_object_unref (tube->public.channel);
+               g_object_unref (tube->public.initiator);
+               g_slice_free (DispatcherTube, tube);
+       }
+}
+
+static void
+dispatcher_tubes_handle_tube_cb (TpProxy      *channel,
+                                const GError *error,
+                                gpointer      user_data,
+                                GObject      *dispatcher)
+{
+       DispatcherTube *tube = user_data;
+
+       if (error) {
+               DEBUG ("Error: %s", error->message);
+       } else {
+               tube->handled = TRUE;
+       }
+}
+
+void
+empathy_dispatcher_tube_process (EmpathyDispatcher     *dispatcher,
+                                EmpathyDispatcherTube *user_data)
+{
+       DispatcherTube *tube = (DispatcherTube*) user_data;
+
+       if (tube->public.activatable) {
+               TpProxy *connection;
+               TpProxy *thandler;
+               gchar   *object_path;
+               guint    handle_type;
+               guint    handle;
+
+               /* Create the proxy for the tube handler */
+               thandler = g_object_new (TP_TYPE_PROXY,
+                                        "dbus-connection", tp_get_bus (),
+                                        "bus-name", tube->bus_name,
+                                        "object-path", tube->object_path,
+                                        NULL);
+               tp_proxy_add_interface_by_id (thandler, EMP_IFACE_QUARK_TUBE_HANDLER);
+
+               /* Give the tube to the handler */
+               g_object_get (tube->public.channel,
+                             "connection", &connection,
+                             "object-path", &object_path,
+                             "handle_type", &handle_type,
+                             "handle", &handle,
+                             NULL);
+
+               DEBUG ("Dispatching tube");
+               emp_cli_tube_handler_call_handle_tube (thandler, -1,
+                                                      connection->bus_name,
+                                                      connection->object_path,
+                                                      object_path, handle_type,
+                                                      handle, tube->public.id,
+                                                      dispatcher_tubes_handle_tube_cb,
+                                                      empathy_dispatcher_tube_ref (user_data),
+                                                      (GDestroyNotify) empathy_dispatcher_tube_unref,
+                                                      G_OBJECT (dispatcher));
+
+               g_object_unref (thandler);
+               g_object_unref (connection);
+               g_free (object_path);
+       }
+}
+
+static void
+dispatcher_tubes_new_tube_cb (TpChannel   *channel,
+                             guint        id,
+                             guint        initiator,
+                             guint        type,
+                             const gchar *service,
+                             GHashTable  *parameters,
+                             guint        state,
+                             gpointer     user_data,
+                             GObject     *dispatcher)
+{
+       EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
+       static TpDBusDaemon   *daemon = NULL;
+       DispatcherTube        *tube;
+       McAccount             *account;
+       guint                  number;
+       gchar                **names;
+       gboolean               running = FALSE;
+       GError                *error = NULL;
+
+       /* Increase tube count */
+       number = GPOINTER_TO_UINT (g_hash_table_lookup (priv->tubes, channel));
+       g_hash_table_replace (priv->tubes, g_object_ref (channel),
+                             GUINT_TO_POINTER (++number));
+       DEBUG ("Increased tube count for channel %p: %d", channel, number);
+
+       /* We dispatch only local pending tubes */
+       if (state != TP_TUBE_STATE_LOCAL_PENDING) {
+               return;
+       }
+
+       if (!daemon) {
+               daemon = tp_dbus_daemon_new (tp_get_bus ());
+       }
+
+       account = empathy_channel_get_account (channel);
+       tube = g_slice_new (DispatcherTube);
+       tube->ref_count = 1;
+       tube->handled = FALSE;
+       tube->factory = empathy_contact_factory_new ();
+       tube->bus_name = empathy_tube_handler_build_bus_name (type, service);
+       tube->object_path = empathy_tube_handler_build_object_path (type, service);
+       tube->public.activatable = FALSE;
+       tube->public.id = id;
+       tube->public.channel = g_object_ref (channel);
+       tube->public.initiator = empathy_contact_factory_get_from_handle (tube->factory,
+                                                                         account,
+                                                                         initiator);
+       g_object_unref (account);
+
+       /* Check if that bus-name has an owner, if it has one that means the
+        * app is already running and we can directly give the channel. */
+       tp_cli_dbus_daemon_run_name_has_owner (daemon, -1, tube->bus_name,
+                                              &running, NULL, NULL);
+       if (running) {
+               DEBUG ("Tube handler running");
+               tube->public.activatable = TRUE;
+               empathy_dispatcher_tube_process (EMPATHY_DISPATCHER (dispatcher),
+                                                (EmpathyDispatcherTube*) tube);
+               empathy_dispatcher_tube_unref ((EmpathyDispatcherTube*) tube);
+               return;
+       }
+
+       /* Check if that bus-name is activatable, if not that means the
+        * application needed to handle this tube isn't installed. */
+       if (!tp_cli_dbus_daemon_run_list_activatable_names (daemon, -1,
+                                                           &names, &error,
+                                                           NULL)) {
+               DEBUG ("Error listing activatable names: %s", error->message);
+               g_clear_error (&error);
+       } else {
+               gchar **name;
+
+               for (name = names; *name; name++) {
+                       if (!tp_strdiff (*name, tube->bus_name)) {
+                               tube->public.activatable = TRUE;
+                               break;
+                       }
+               }
+               g_strfreev (names);
+       }
+
+       g_signal_emit (dispatcher, signals[FILTER_TUBE], 0, tube);
+       empathy_dispatcher_tube_unref ((EmpathyDispatcherTube*) tube);
+}
+
+static void
+dispatcher_tubes_list_tubes_cb (TpChannel       *channel,
+                               const GPtrArray *tubes,
+                               const GError    *error,
+                               gpointer         user_data,
+                               GObject         *dispatcher)
+{
+       guint i;
+
+       if (error) {
+               DEBUG ("Error: %s", error->message);
+               return;
+       }
+
+       for (i = 0; i < tubes->len; i++) {
+               GValueArray *values;
+
+               values = g_ptr_array_index (tubes, i);
+               dispatcher_tubes_new_tube_cb (channel,
+                                             g_value_get_uint (g_value_array_get_nth (values, 0)),
+                                             g_value_get_uint (g_value_array_get_nth (values, 1)),
+                                             g_value_get_uint (g_value_array_get_nth (values, 2)),
+                                             g_value_get_string (g_value_array_get_nth (values, 3)),
+                                             g_value_get_boxed (g_value_array_get_nth (values, 4)),
+                                             g_value_get_uint (g_value_array_get_nth (values, 5)),
+                                             user_data, dispatcher);
+       }
+}
+
+static void
+dispatcher_tubes_channel_invalidated_cb (TpProxy       *proxy,
+                                        guint          domain,
+                                        gint           code,
+                                        gchar         *message,
+                                        EmpathyDispatcher *dispatcher)
+{
+       EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
+
+       DEBUG ("Error: %s", message);
+
+       g_hash_table_remove (priv->tubes, proxy);
+}
+
+static void
+dispatcher_tubes_tube_closed_cb (TpChannel *channel,
+                                guint      id,
+                                gpointer   user_data,
+                                GObject   *dispatcher)
+{
+       EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
+       guint                  number;
+
+       number = GPOINTER_TO_UINT (g_hash_table_lookup (priv->tubes, channel));
+       if (number == 1) {
+               DEBUG ("No more tube, closing channel");
+               tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);
+       }
+       else if (number > 1) {
+               DEBUG ("Decrease tube count: %d", number);
+               g_hash_table_replace (priv->tubes, g_object_ref (channel),
+                                     GUINT_TO_POINTER (--number));
+       }
+}
+
+static void
+dispatcher_tubes_handle_channel (EmpathyDispatcher *dispatcher,
+                                TpChannel         *channel)
+{
+       EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
+
+       if (g_hash_table_lookup (priv->tubes, channel)) {
+               return;
+       }
+
+       DEBUG ("Handling new channel");
+
+       g_hash_table_insert (priv->tubes, g_object_ref (channel),
+                            GUINT_TO_POINTER (0));
+
+       g_signal_connect (channel, "invalidated",
+                         G_CALLBACK (dispatcher_tubes_channel_invalidated_cb),
+                         dispatcher);
+
+       tp_cli_channel_type_tubes_connect_to_tube_closed (channel,
+                                                         dispatcher_tubes_tube_closed_cb,
+                                                         NULL, NULL,
+                                                         G_OBJECT (dispatcher), NULL);
+       tp_cli_channel_type_tubes_connect_to_new_tube (channel,
+                                                      dispatcher_tubes_new_tube_cb,
+                                                      NULL, NULL,
+                                                      G_OBJECT (dispatcher), NULL);
+       tp_cli_channel_type_tubes_call_list_tubes (channel, -1,
+                                                  dispatcher_tubes_list_tubes_cb,
+                                                  NULL, NULL,
+                                                  G_OBJECT (dispatcher));
+}
+
+static void
+dispatcher_connection_invalidated_cb (TpConnection  *connection,
+                                     guint          domain,
+                                     gint           code,
+                                     gchar         *message,
+                                     EmpathyDispatcher *dispatcher)
+{
+       EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
+       GHashTableIter         iter;
+       gpointer               key, value;
+
+       DEBUG ("Error: %s", message);
+
+       g_hash_table_iter_init (&iter, priv->connections);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               if (value == connection) {
+                       g_hash_table_remove (priv->connections, key);
+                       break;
+               }
+       }
+}
+
+static void
+dispatcher_connection_new_channel_cb (TpConnection *connection,
+                                     const gchar  *object_path,
+                                     const gchar  *channel_type,
+                                     guint         handle_type,
+                                     guint         handle,
+                                     gboolean      suppress_handler,
+                                     gpointer      user_data,
+                                     GObject      *object)
+{
+       EmpathyDispatcher *dispatcher = EMPATHY_DISPATCHER (object);
+       TpChannel         *channel;
+       gpointer           had_channels;
+
+       had_channels = g_object_get_data (G_OBJECT (connection), "had-channels");
+       if (had_channels == NULL) {
+               /* ListChannels didn't return yet, return to avoid duplicate
+                * dispatching */
+               return;
+       }
+
+       channel = tp_channel_new (connection, object_path, channel_type,
+                                 handle_type, handle, NULL);
+       tp_channel_run_until_ready (channel, NULL, NULL);
+
+       if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES)) {
+               dispatcher_tubes_handle_channel (dispatcher, channel);
+       }
+
+       if (suppress_handler) {
+               g_signal_emit (dispatcher, signals[DISPATCH_CHANNEL], 0, channel);
+       } else {
+               g_signal_emit (dispatcher, signals[FILTER_CHANNEL], 0, channel);
+       }
+
+       g_object_unref (channel);
+}
+
+static void
+dispatcher_connection_list_channels_cb (TpConnection    *connection,
+                                       const GPtrArray *channels,
+                                       const GError    *error,
+                                       gpointer         user_data,
+                                       GObject         *dispatcher)
+{
+       guint i;
+
+       if (error) {
+               DEBUG ("Error: %s", error->message);
+               return;
+       }
+
+       g_object_set_data (G_OBJECT (connection), "had-channels",
+                          GUINT_TO_POINTER (1));
+
+       for (i = 0; i < channels->len; i++) {
+               GValueArray *values;
+
+               values = g_ptr_array_index (channels, i);
+               dispatcher_connection_new_channel_cb (connection,
+                       g_value_get_boxed (g_value_array_get_nth (values, 0)),
+                       g_value_get_string (g_value_array_get_nth (values, 1)),
+                       g_value_get_uint (g_value_array_get_nth (values, 2)),
+                       g_value_get_uint (g_value_array_get_nth (values, 3)),
+                       FALSE, user_data, dispatcher);
+       }
+}
+
+static void
+dispatcher_connection_advertise_capabilities_cb (TpConnection    *connection,
+                                                const GPtrArray *capabilities,
+                                                const GError    *error,
+                                                gpointer         user_data,
+                                                GObject         *dispatcher)
+{
+       if (error) {
+               DEBUG ("Error: %s", error->message);
+       }
+}
+
+static void
+dispatcher_connection_ready_cb (TpConnection  *connection,
+                               const GError  *error,
+                               gpointer       dispatcher)
+{
+       GPtrArray   *capabilities;
+       GType        cap_type;
+       GValue       cap = {0, };
+       const gchar *remove = NULL;
+
+       if (error) {
+               dispatcher_connection_invalidated_cb (connection,
+                                                     error->domain,
+                                                     error->code,
+                                                     error->message,
+                                                     dispatcher);
+               return;
+       }
+
+       g_signal_connect (connection, "invalidated",
+                         G_CALLBACK (dispatcher_connection_invalidated_cb),
+                         dispatcher);
+       tp_cli_connection_connect_to_new_channel (connection,
+                                                 dispatcher_connection_new_channel_cb,
+                                                 NULL, NULL,
+                                                 G_OBJECT (dispatcher), NULL);
+       tp_cli_connection_call_list_channels (connection, -1,
+                                             dispatcher_connection_list_channels_cb,
+                                             NULL, NULL,
+                                             G_OBJECT (dispatcher));
+
+       /* Advertise VoIP capabilities */
+       capabilities = g_ptr_array_sized_new (1);
+       cap_type = dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING,
+                                          G_TYPE_UINT, G_TYPE_INVALID);
+       g_value_init (&cap, cap_type);
+       g_value_take_boxed (&cap, dbus_g_type_specialized_construct (cap_type));
+       dbus_g_type_struct_set (&cap,
+                               0, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
+                               1, TP_CHANNEL_MEDIA_CAPABILITY_AUDIO |
+                                  TP_CHANNEL_MEDIA_CAPABILITY_VIDEO |
+                                  TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_STUN  |
+                                  TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_GTALK_P2P,
+                               G_MAXUINT);
+       g_ptr_array_add (capabilities, g_value_get_boxed (&cap));
+
+       tp_cli_connection_interface_capabilities_call_advertise_capabilities (
+               connection, -1,
+               capabilities, &remove,
+               dispatcher_connection_advertise_capabilities_cb,
+               NULL, NULL, G_OBJECT (dispatcher));
+       /* FIXME: Is that leaked? */
+}
+
+static void
+dispatcher_update_account (EmpathyDispatcher *dispatcher,
+                          McAccount         *account)
+{
+       EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
+       TpConnection          *connection;
+
+       connection = g_hash_table_lookup (priv->connections, account);
+       if (connection) {
+               return;
+       }
+
+       connection = mission_control_get_tpconnection (priv->mc, account, NULL);
+       if (!connection) {
+               return;
+       }
+
+       g_hash_table_insert (priv->connections, g_object_ref (account), connection);
+       tp_connection_call_when_ready (connection,
+                                      dispatcher_connection_ready_cb,
+                                      dispatcher);
+}
+
+static void
+dispatcher_status_changed_cb (MissionControl           *mc,
+                             TpConnectionStatus        status,
+                             McPresence                presence,
+                             TpConnectionStatusReason  reason,
+                             const gchar              *unique_name,
+                             EmpathyDispatcher            *dispatcher)
+{
+       McAccount *account;
+
+       account = mc_account_lookup (unique_name);
+       dispatcher_update_account (dispatcher, account);
+       g_object_unref (account);
+}
+
+static guint
+dispatcher_channel_hash (gconstpointer key)
+{
+       TpProxy *channel = TP_PROXY (key);
+
+       return g_str_hash (channel->object_path);
+}
+
+static gboolean
+dispatcher_channel_equal (gconstpointer a,
+                     gconstpointer b)
+{
+       TpProxy *channel_a = TP_PROXY (a);
+       TpProxy *channel_b = TP_PROXY (b);
+
+       return g_str_equal (channel_a->object_path, channel_b->object_path);
+}
+
+static void
+dispatcher_finalize (GObject *object)
+{
+       EmpathyDispatcherPriv *priv = GET_PRIV (object);
+
+       empathy_disconnect_account_status_changed (priv->token);
+       g_object_unref (priv->mc);
+
+       g_hash_table_destroy (priv->connections);
+       g_hash_table_destroy (priv->tubes);
+}
+
+static void
+empathy_dispatcher_class_init (EmpathyDispatcherClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->finalize = dispatcher_finalize;
+
+       signals[DISPATCH_CHANNEL] =
+               g_signal_new ("dispatch-channel",
+                             G_TYPE_FROM_CLASS (klass),
+                             G_SIGNAL_RUN_LAST,
+                             0,
+                             NULL, NULL,
+                             g_cclosure_marshal_VOID__OBJECT,
+                             G_TYPE_NONE,
+                             1, TP_TYPE_CHANNEL);
+       signals[FILTER_CHANNEL] =
+               g_signal_new ("filter-channel",
+                             G_TYPE_FROM_CLASS (klass),
+                             G_SIGNAL_RUN_LAST,
+                             0,
+                             NULL, NULL,
+                             g_cclosure_marshal_VOID__OBJECT,
+                             G_TYPE_NONE,
+                             1, TP_TYPE_CHANNEL);
+       signals[FILTER_TUBE] =
+               g_signal_new ("filter-tube",
+                             G_TYPE_FROM_CLASS (klass),
+                             G_SIGNAL_RUN_LAST,
+                             0,
+                             NULL, NULL,
+                             g_cclosure_marshal_VOID__BOXED,
+                             G_TYPE_NONE,
+                             1, EMPATHY_TYPE_DISPATCHER_TUBE);
+
+       g_type_class_add_private (object_class, sizeof (EmpathyDispatcherPriv));
+}
+
+static void
+empathy_dispatcher_init (EmpathyDispatcher *dispatcher)
+{
+       GList                 *accounts, *l;
+       EmpathyDispatcherPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (dispatcher,
+               EMPATHY_TYPE_DISPATCHER, EmpathyDispatcherPriv);
+
+       dispatcher->priv = priv;
+       priv->tubes = g_hash_table_new_full (dispatcher_channel_hash,
+                                            dispatcher_channel_equal,
+                                            g_object_unref, NULL);
+
+       priv->mc = empathy_mission_control_new ();
+       priv->token = empathy_connect_to_account_status_changed (priv->mc,
+               G_CALLBACK (dispatcher_status_changed_cb),
+               dispatcher, NULL);
+
+       priv->connections = g_hash_table_new_full (empathy_account_hash,
+                                                  empathy_account_equal,
+                                                  g_object_unref,
+                                                  g_object_unref);
+       accounts = mc_accounts_list_by_enabled (TRUE);
+       for (l = accounts; l; l = l->next) {
+               dispatcher_update_account (dispatcher, l->data);
+               g_object_unref (l->data);
+       }
+       g_list_free (accounts);
+}
+
+EmpathyDispatcher *
+empathy_dispatcher_new (void)
+{
+       if (!dispatcher) {
+               dispatcher = g_object_new (EMPATHY_TYPE_DISPATCHER, NULL);
+               g_object_add_weak_pointer (G_OBJECT (dispatcher), (gpointer) &dispatcher);
+       } else {
+               g_object_ref (dispatcher);
+       }
+
+       return dispatcher;
+}
+
diff --git a/libempathy/empathy-dispatcher.h b/libempathy/empathy-dispatcher.h
new file mode 100644 (file)
index 0000000..0cc59c8
--- /dev/null
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007-2008 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#ifndef __EMPATHY_DISPATCHER_H__
+#define __EMPATHY_DISPATCHER_H__
+
+#include <glib.h>
+
+#include <telepathy-glib/channel.h>
+
+#include "empathy-contact.h"
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_DISPATCHER         (empathy_dispatcher_get_type ())
+#define EMPATHY_DISPATCHER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_DISPATCHER, EmpathyDispatcher))
+#define EMPATHY_DISPATCHER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_DISPATCHER, EmpathyDispatcherClass))
+#define EMPATHY_IS_DISPATCHER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_DISPATCHER))
+#define EMPATHY_IS_DISPATCHER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_DISPATCHER))
+#define EMPATHY_DISPATCHER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_DISPATCHER, EmpathyDispatcherClass))
+
+typedef struct _EmpathyDispatcher      EmpathyDispatcher;
+typedef struct _EmpathyDispatcherClass EmpathyDispatcherClass;
+
+struct _EmpathyDispatcher {
+       GObject parent;
+       gpointer priv;
+};
+
+struct _EmpathyDispatcherClass {
+       GObjectClass parent_class;
+};
+
+#define EMPATHY_TYPE_DISPATCHER_TUBE (empathy_dispatcher_tube_get_type ())
+typedef struct {
+       EmpathyContact *initiator;
+       TpChannel      *channel;
+       guint           id;
+       gboolean        activatable;
+} EmpathyDispatcherTube;
+
+GType                  empathy_dispatcher_get_type       (void) G_GNUC_CONST;
+EmpathyDispatcher *    empathy_dispatcher_new            (void);
+void                   empathy_dispatcher_channel_process(EmpathyDispatcher     *dispatcher,
+                                                         TpChannel             *channel);
+GType                  empathy_dispatcher_tube_get_type  (void);
+EmpathyDispatcherTube *empathy_dispatcher_tube_ref       (EmpathyDispatcherTube *tube);
+void                   empathy_dispatcher_tube_unref     (EmpathyDispatcherTube *tube);
+void                   empathy_dispatcher_tube_process   (EmpathyDispatcher     *dispatcher,
+                                                         EmpathyDispatcherTube *tube);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_DISPATCHER_H__ */
index f82bac38eb7978b56113414ee0ff7a8a3448b706..dc87087ec1ceb00e0c0a1b736779fe5f41ec11ac 100644 (file)
@@ -31,7 +31,6 @@ empathy_SOURCES =                                                     \
        empathy-main-window.c empathy-main-window.h                     \
        empathy-preferences.c empathy-preferences.h                     \
        empathy-call-window.c empathy-call-window.h                     \
-       empathy-filter.c empathy-filter.h                               \
        ephy-spinner.c ephy-spinner.h
 
 empathy_accounts_SOURCES = empathy-accounts.c
diff --git a/src/empathy-filter.c b/src/empathy-filter.c
deleted file mode 100644 (file)
index e1305fe..0000000
+++ /dev/null
@@ -1,1001 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2007-2008 Collabora Ltd.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Authors: Xavier Claessens <xclaesse@gmail.com>
- */
-
-#include <config.h>
-
-#include <string.h>
-
-#include <glib/gi18n.h>
-
-#include <telepathy-glib/enums.h>
-#include <telepathy-glib/channel.h>
-#include <telepathy-glib/connection.h>
-#include <telepathy-glib/util.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/proxy-subclass.h>
-
-#include <libmissioncontrol/mission-control.h>
-#include <libmissioncontrol/mc-account.h>
-
-#include <extensions/extensions.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-tube-handler.h>
-#include <libempathy/empathy-contact-factory.h>
-
-#include <libempathy-gtk/empathy-chat.h>
-#include <libempathy-gtk/empathy-images.h>
-#include <libempathy-gtk/empathy-contact-dialogs.h>
-
-#include "empathy-filter.h"
-#include "empathy-chat-window.h"
-#include "empathy-call-window.h"
-
-#define DEBUG_FLAG EMPATHY_DEBUG_FILTER
-#include <libempathy/empathy-debug.h>
-
-#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyFilter)
-typedef struct {
-       GSList         *events;
-       GHashTable     *accounts;
-       gpointer        token;
-       MissionControl *mc;
-       GHashTable     *tubes;
-} EmpathyFilterPriv;
-
-G_DEFINE_TYPE (EmpathyFilter, empathy_filter, G_TYPE_OBJECT);
-
-enum {
-       PROP_0,
-       PROP_TOP_EVENT,
-};
-
-typedef void (*FilterFunc) (EmpathyFilter *filter,
-                           gpointer       user_data);
-
-typedef struct {
-       EmpathyFilterEvent public;
-       FilterFunc         func;
-       gpointer           user_data;
-} EmpathyFilterEventExt;
-
-static guint
-filter_channel_hash (gconstpointer key)
-{
-       TpProxy *channel = TP_PROXY (key);
-
-       return g_str_hash (channel->object_path);
-}
-
-static gboolean
-filter_channel_equal (gconstpointer a,
-                     gconstpointer b)
-{
-       TpProxy *channel_a = TP_PROXY (a);
-       TpProxy *channel_b = TP_PROXY (b);
-
-       return g_str_equal (channel_a->object_path, channel_b->object_path);
-}
-
-static void
-filter_event_free (EmpathyFilterEventExt *event)
-{
-       g_free (event->public.icon_name);
-       g_free (event->public.message);
-       g_slice_free (EmpathyFilterEventExt, event);
-}
-
-static void
-filter_emit_event (EmpathyFilter *filter,
-                  const gchar   *icon_name,
-                  const gchar   *message,
-                  FilterFunc     func,
-                  gpointer       user_data)
-{
-       EmpathyFilterPriv     *priv = GET_PRIV (filter);
-       EmpathyFilterEventExt *event;
-
-       DEBUG ("Emit event, icon_name=%s message='%s'", icon_name, message);
-
-       event = g_slice_new0 (EmpathyFilterEventExt);
-       event->func = func;
-       event->user_data = user_data;
-       event->public.icon_name = g_strdup (icon_name);
-       event->public.message = g_strdup (message);
-
-       priv->events = g_slist_append (priv->events, event);
-       if (priv->events->data == event) {
-               g_object_notify (G_OBJECT (filter), "top-event");
-       }
-}
-
-void
-empathy_filter_activate_event (EmpathyFilter      *filter,
-                              EmpathyFilterEvent *event)
-{
-       EmpathyFilterPriv     *priv = GET_PRIV (filter);
-       EmpathyFilterEventExt *event_ext;
-       GSList                *l;
-       gboolean               is_top;
-
-       g_return_if_fail (EMPATHY_IS_FILTER (filter));
-       g_return_if_fail (event != NULL);
-
-       if (!(l = g_slist_find (priv->events, event))) {
-               return;
-       }
-
-       DEBUG ("Activating event");
-
-       is_top = (l == priv->events);
-       priv->events = g_slist_delete_link (priv->events, l);
-       if (is_top) {
-               g_object_notify (G_OBJECT (filter), "top-event");
-       }
-
-       event_ext = (EmpathyFilterEventExt*) event;
-       if (event_ext->func) {
-               event_ext->func (filter, event_ext->user_data);
-       }
-
-       filter_event_free (event_ext);
-}
-
-EmpathyFilterEvent *
-empathy_filter_get_top_event (EmpathyFilter *filter)
-{
-       EmpathyFilterPriv *priv = GET_PRIV (filter);
-
-       g_return_val_if_fail (EMPATHY_IS_FILTER (filter), NULL);
-
-       return priv->events ? priv->events->data : NULL;
-}
-
-static void
-filter_chat_dispatch (EmpathyFilter *filter,
-                     gpointer       user_data)
-{
-       EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
-       EmpathyChat   *chat = NULL;
-       const gchar   *id;
-
-       id = empathy_tp_chat_get_id (tp_chat);
-       if (!id) {
-               EmpathyContact *contact;
-
-               contact = empathy_tp_chat_get_remote_contact (tp_chat);
-               if (contact) {
-                       id = empathy_contact_get_id (contact);
-               }
-       }
-
-       if (id) {
-               McAccount *account;
-
-               account = empathy_tp_chat_get_account (tp_chat);
-               chat = empathy_chat_window_find_chat (account, id);
-       }
-
-       if (chat) {
-               empathy_chat_set_tp_chat (chat, tp_chat);
-       } else {
-               chat = empathy_chat_new (tp_chat);
-       }
-
-       empathy_chat_window_present_chat (chat);
-       g_object_unref (tp_chat);
-}
-
-static void
-filter_chat_message_received_cb (EmpathyTpChat   *tp_chat,
-                                EmpathyMessage  *message,
-                                EmpathyFilter   *filter)
-{
-       EmpathyContact  *sender;
-       gchar           *msg;
-
-       g_signal_handlers_disconnect_by_func (tp_chat,
-                                             filter_chat_message_received_cb,
-                                             filter);
-
-       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));
-
-       filter_emit_event (filter, EMPATHY_IMAGE_NEW_MESSAGE, msg,
-                          filter_chat_dispatch, tp_chat);
-
-       g_free (msg);
-}
-
-static void
-filter_chat_handle_channel (EmpathyFilter *filter,
-                           TpChannel     *channel,
-                           gboolean       is_incoming)
-{
-       EmpathyTpChat *tp_chat;
-
-       DEBUG ("New text channel to be filtered: %p", channel);
-
-       tp_chat = empathy_tp_chat_new (channel, FALSE);
-       empathy_run_until_ready (tp_chat);
-       if (is_incoming) {
-               filter_chat_dispatch (filter, tp_chat);
-       } else {
-               g_signal_connect (tp_chat, "message-received",
-                                 G_CALLBACK (filter_chat_message_received_cb),
-                                 filter);
-       }
-}
-
-static void
-filter_call_dispatch (EmpathyFilter *filter,
-                     gpointer       user_data)
-{
-       EmpathyTpCall *call = EMPATHY_TP_CALL (user_data);
-
-       empathy_call_window_new (call);
-       g_object_unref (call);
-}
-
-static void
-filter_call_contact_notify_cb (EmpathyTpCall *call,
-                              gpointer       unused,
-                              EmpathyFilter *filter)
-{
-       EmpathyContact *contact;
-       gchar          *msg;
-
-       g_object_get (call, "contact", &contact, NULL);
-       if (!contact) {
-               return;
-       }
-
-       empathy_contact_run_until_ready (contact,
-                                        EMPATHY_CONTACT_READY_NAME,
-                                        NULL);
-
-       msg = g_strdup_printf (_("Incoming call from %s"),
-                              empathy_contact_get_name (contact));
-
-       filter_emit_event (filter, EMPATHY_IMAGE_VOIP, msg,
-                          filter_call_dispatch, call);
-
-       g_free (msg);
-       g_object_unref (contact);
-}
-
-static void
-filter_call_handle_channel (EmpathyFilter *filter,
-                           TpChannel     *channel,
-                           gboolean       is_incoming)
-{
-       EmpathyTpCall *call;
-
-       DEBUG ("New media channel to be filtered: %p", channel);
-
-       call = empathy_tp_call_new (channel);
-       if (is_incoming) {
-               filter_call_dispatch (filter, call);
-       } else {
-               g_signal_connect (call, "notify::contact",
-                                 G_CALLBACK (filter_call_contact_notify_cb),
-                                 filter);      
-       }
-}
-
-static void
-filter_contact_list_subscribe (EmpathyFilter *filter,
-                              gpointer       user_data)
-{
-       EmpathyContact *contact = EMPATHY_CONTACT (user_data);
-
-       empathy_subscription_dialog_show (contact, NULL);
-       g_object_unref (contact);
-}
-
-static void
-filter_contact_list_local_pending_cb (EmpathyTpGroup *group,
-                                     EmpathyContact *contact,
-                                     EmpathyContact *actor,
-                                     guint           reason,
-                                     gchar          *message,
-                                     EmpathyFilter  *filter)
-{
-       GString *str;
-
-       DEBUG ("New local pending contact");
-
-       empathy_contact_run_until_ready (contact,
-                                        EMPATHY_CONTACT_READY_NAME,
-                                        NULL);
-
-       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);
-       }
-
-       filter_emit_event (filter, GTK_STOCK_DIALOG_QUESTION, str->str,
-                          filter_contact_list_subscribe,
-                          g_object_ref (contact));
-
-       g_string_free (str, TRUE);
-}
-
-static void
-filter_contact_list_ready_cb (EmpathyTpGroup *group,
-                             gpointer        unused,
-                             EmpathyFilter  *filter)
-{
-       GList *pendings, *l;
-
-       if (tp_strdiff ("publish", empathy_tp_group_get_name (group))) {
-               g_object_unref (group);
-               return;
-       }
-
-       DEBUG ("Publish contact list ready");
-
-       g_signal_connect (group, "local-pending",
-                         G_CALLBACK (filter_contact_list_local_pending_cb),
-                         filter);
-
-       pendings = empathy_tp_group_get_local_pendings (group);
-       for (l = pendings; l; l = l->next) {
-               EmpathyPendingInfo *info = l->data;
-
-               filter_contact_list_local_pending_cb (group, info->member,
-                                                     info->actor, info->reason,
-                                                     info->message, filter);
-               empathy_pending_info_free (info);
-       }
-       g_list_free (pendings);
-}
-
-static void
-filter_tubes_async_cb (TpProxy      *channel,
-                      const GError *error,
-                      gpointer      user_data,
-                      GObject      *filter)
-{
-       if (error) {
-               DEBUG ("Error %s: %s", (gchar*) user_data, error->message);
-       }
-}
-
-typedef struct {
-       EmpathyContactFactory *factory;
-       EmpathyContact        *initiator;
-       TpChannel             *channel;
-       guint                  id;
-       gchar                 *bus_name;
-       gchar                 *object_path;
-       gboolean               activatable;
-} FilterTubesData;
-
-static void
-filter_tubes_dispatch (EmpathyFilter *filter,
-                      gpointer       user_data)
-{
-       FilterTubesData *data = user_data;
-
-       if (data->activatable) {
-               TpProxy *connection;
-               TpProxy *thandler;
-               gchar   *object_path;
-               guint    handle_type;
-               guint    handle;
-
-               /* Create the proxy for the tube handler */
-               thandler = g_object_new (TP_TYPE_PROXY,
-                                        "dbus-connection", tp_get_bus (),
-                                        "bus-name", data->bus_name,
-                                        "object-path", data->object_path,
-                                        NULL);
-               tp_proxy_add_interface_by_id (thandler, EMP_IFACE_QUARK_TUBE_HANDLER);
-
-               /* Give the tube to the handler */
-               g_object_get (data->channel,
-                             "connection", &connection,
-                             "object-path", &object_path,
-                             "handle_type", &handle_type,
-                             "handle", &handle,
-                             NULL);
-
-               DEBUG ("Dispatching tube");
-               emp_cli_tube_handler_call_handle_tube (thandler, -1,
-                                                      connection->bus_name,
-                                                      connection->object_path,
-                                                      object_path, handle_type,
-                                                      handle, data->id,
-                                                      filter_tubes_async_cb,
-                                                      "handling tube", NULL,
-                                                      G_OBJECT (filter));
-
-               g_object_unref (thandler);
-               g_object_unref (connection);
-               g_free (object_path);
-       } else {
-               GtkWidget *dialog;
-               gchar     *str;
-
-               /* Tell the user that the tube can't be handled */
-               str = g_strdup_printf (_("%s offered you an invitation, but "
-                                        "you don't have the needed external "
-                                        "application to handle it."),
-                                      empathy_contact_get_name (data->initiator));
-
-               dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
-                                                GTK_MESSAGE_ERROR,
-                                                GTK_BUTTONS_OK, 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);
-
-               DEBUG ("Tube can't be handled, closing");
-               tp_cli_channel_type_tubes_call_close_tube (data->channel, -1,
-                                                          data->id,
-                                                          NULL, NULL, NULL,
-                                                          NULL);
-       }
-
-       g_free (data->bus_name);
-       g_free (data->object_path);
-       g_object_unref (data->channel);
-       g_object_unref (data->initiator);
-       g_object_unref (data->factory);
-       g_slice_free (FilterTubesData, data);
-}
-
-static void
-filter_tubes_new_tube_cb (TpChannel   *channel,
-                         guint        id,
-                         guint        initiator,
-                         guint        type,
-                         const gchar *service,
-                         GHashTable  *parameters,
-                         guint        state,
-                         gpointer     user_data,
-                         GObject     *filter)
-{
-       EmpathyFilterPriv *priv = GET_PRIV (filter);
-       static TpDBusDaemon *daemon = NULL;
-       FilterTubesData   *data;
-       McAccount         *account;
-       guint              number;
-       gchar             *msg;
-       gchar            **names;
-       gboolean           running = FALSE;
-       const gchar       *icon_name;
-       GError            *error = NULL;
-
-       /* Increase tube count */
-       number = GPOINTER_TO_UINT (g_hash_table_lookup (priv->tubes, channel));
-       g_hash_table_replace (priv->tubes, g_object_ref (channel),
-                             GUINT_TO_POINTER (++number));
-       DEBUG ("Increased tube count for channel %p: %d", channel, number);
-
-       /* We dispatch only local pending tubes */
-       if (state != TP_TUBE_STATE_LOCAL_PENDING) {
-               return;
-       }
-
-       if (!daemon) {
-               daemon = tp_dbus_daemon_new (tp_get_bus ());
-       }
-
-       account = empathy_channel_get_account (channel);
-       data = g_slice_new (FilterTubesData);
-       data->activatable = FALSE;
-       data->id = id;
-       data->channel = g_object_ref (channel);
-       data->factory = empathy_contact_factory_new ();
-       data->initiator = empathy_contact_factory_get_from_handle (data->factory,
-                                                                  account,
-                                                                  initiator);
-       data->bus_name = empathy_tube_handler_build_bus_name (type, service);
-       data->object_path = empathy_tube_handler_build_object_path (type, service);
-       g_object_unref (account);
-
-       /* Check if that bus-name has an owner, if it has one that means the
-        * app is already running and we can directly give the channel. */
-       tp_cli_dbus_daemon_run_name_has_owner (daemon, -1, data->bus_name,
-                                              &running, NULL, NULL);
-       if (running) {
-               DEBUG ("Tube handler running");
-               data->activatable = TRUE;
-               filter_tubes_dispatch (EMPATHY_FILTER (filter), data);
-               return;
-       }
-
-       /* Check if that bus-name is activatable, if not that means the
-        * application needed to handle this tube isn't installed. */
-       if (!tp_cli_dbus_daemon_run_list_activatable_names (daemon, -1,
-                                                           &names, &error,
-                                                           NULL)) {
-               DEBUG ("Error listing activatable names: %s", error->message);
-               g_clear_error (&error);
-       } else {
-               gchar **name;
-
-               for (name = names; *name; name++) {
-                       if (!tp_strdiff (*name, data->bus_name)) {
-                               data->activatable = TRUE;
-                               break;
-                       }
-               }
-               g_strfreev (names);
-       }
-
-       empathy_contact_run_until_ready (data->initiator,
-                                        EMPATHY_CONTACT_READY_NAME, NULL);
-
-       if (data->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 (data->initiator));
-       } else {
-               icon_name = GTK_STOCK_DIALOG_ERROR;
-               msg = g_strdup_printf (_("%s is offering you an invitation, but "
-                                        "you don't have the needed external "
-                                        "application to handle it."),
-                                      empathy_contact_get_name (data->initiator));
-       }
-
-       filter_emit_event (EMPATHY_FILTER (filter), icon_name, msg,
-                          filter_tubes_dispatch, data);
-
-       g_free (msg);
-}
-
-static void
-filter_tubes_list_tubes_cb (TpChannel       *channel,
-                           const GPtrArray *tubes,
-                           const GError    *error,
-                           gpointer         user_data,
-                           GObject         *filter)
-{
-       guint i;
-
-       if (error) {
-               DEBUG ("Error listing tubes: %s", error->message);
-               return;
-       }
-
-       for (i = 0; i < tubes->len; i++) {
-               GValueArray *values;
-
-               values = g_ptr_array_index (tubes, i);
-               filter_tubes_new_tube_cb (channel,
-                                         g_value_get_uint (g_value_array_get_nth (values, 0)),
-                                         g_value_get_uint (g_value_array_get_nth (values, 1)),
-                                         g_value_get_uint (g_value_array_get_nth (values, 2)),
-                                         g_value_get_string (g_value_array_get_nth (values, 3)),
-                                         g_value_get_boxed (g_value_array_get_nth (values, 4)),
-                                         g_value_get_uint (g_value_array_get_nth (values, 5)),
-                                         user_data, filter);
-       }
-}
-
-static void
-filter_tubes_channel_invalidated_cb (TpProxy       *proxy,
-                                    guint          domain,
-                                    gint           code,
-                                    gchar         *message,
-                                    EmpathyFilter *filter)
-{
-       EmpathyFilterPriv *priv = GET_PRIV (filter);
-
-       DEBUG ("Channel %p invalidated: %s", proxy, message);
-
-       g_hash_table_remove (priv->tubes, proxy);
-}
-
-static void
-filter_tubes_tube_closed_cb (TpChannel *channel,
-                            guint      id,
-                            gpointer   user_data,
-                            GObject   *filter)
-{
-       EmpathyFilterPriv *priv = GET_PRIV (filter);
-       guint              number;
-
-       number = GPOINTER_TO_UINT (g_hash_table_lookup (priv->tubes, channel));
-       if (number == 1) {
-               DEBUG ("Ended tube count for channel %p, closing channel",
-                       channel);
-               tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);
-       }
-       else if (number > 1) {
-               DEBUG ("Decrease tube count for channel %p: %d", channel, number);
-               g_hash_table_replace (priv->tubes, g_object_ref (channel),
-                                     GUINT_TO_POINTER (--number));
-       }
-}
-
-static void
-filter_tubes_handle_channel (EmpathyFilter *filter,
-                            TpChannel     *channel,
-                            gboolean       is_incoming)
-{
-       EmpathyFilterPriv *priv = GET_PRIV (filter);
-
-       if (g_hash_table_lookup (priv->tubes, channel)) {
-               return;
-       }
-
-       DEBUG ("Handling new channel");
-
-       g_hash_table_insert (priv->tubes, g_object_ref (channel),
-                            GUINT_TO_POINTER (0));
-
-       g_signal_connect (channel, "invalidated",
-                         G_CALLBACK (filter_tubes_channel_invalidated_cb),
-                         filter);
-
-       tp_cli_channel_type_tubes_connect_to_tube_closed (channel,
-                                                         filter_tubes_tube_closed_cb,
-                                                         NULL, NULL,
-                                                         G_OBJECT (filter), NULL);
-       tp_cli_channel_type_tubes_connect_to_new_tube (channel,
-                                                      filter_tubes_new_tube_cb,
-                                                      NULL, NULL,
-                                                      G_OBJECT (filter), NULL);
-       tp_cli_channel_type_tubes_call_list_tubes (channel, -1,
-                                                  filter_tubes_list_tubes_cb,
-                                                  NULL, NULL,
-                                                  G_OBJECT (filter));
-}
-
-static void
-filter_contact_list_destroy_cb (EmpathyTpGroup *group,
-                               EmpathyFilter  *filter)
-{
-       g_object_unref (group);
-}
-
-static void
-filter_contact_list_handle_channel (EmpathyFilter *filter,
-                                   TpChannel     *channel,
-                                   gboolean       is_incoming)
-{
-       EmpathyTpGroup *group;
-
-       group = empathy_tp_group_new (channel);
-       g_signal_connect (group, "notify::ready",
-                         G_CALLBACK (filter_contact_list_ready_cb),
-                         filter);      
-       g_signal_connect (group, "destroy",
-                         G_CALLBACK (filter_contact_list_destroy_cb),
-                         filter);
-}
-
-static void
-filter_connection_invalidated_cb (TpConnection  *connection,
-                                 guint          domain,
-                                 gint           code,
-                                 gchar         *message,
-                                 EmpathyFilter *filter)
-{
-       EmpathyFilterPriv *priv = GET_PRIV (filter);
-       GHashTableIter     iter;
-       gpointer           key, value;
-
-       DEBUG ("connection invalidated: %s", message);
-
-       g_hash_table_iter_init (&iter, priv->accounts);
-       while (g_hash_table_iter_next (&iter, &key, &value)) {
-               if (value == connection) {
-                       g_hash_table_remove (priv->accounts, key);
-                       break;
-               }
-       }
-}
-
-typedef void (*HandleChannelFunc) (EmpathyFilter *filter,
-                                  TpChannel     *channel,
-                                  gboolean       is_incoming);
-
-static void
-filter_conection_new_channel_cb (TpConnection *connection,
-                                const gchar  *object_path,
-                                const gchar  *channel_type,
-                                guint         handle_type,
-                                guint         handle,
-                                gboolean      suppress_handler,
-                                gpointer      user_data,
-                                GObject      *filter)
-{
-       HandleChannelFunc  func = NULL;
-       TpChannel         *channel;
-       gpointer           had_channels;
-       
-       had_channels = g_object_get_data (G_OBJECT (connection), "had-channels");
-       if (had_channels == NULL) {
-               return;
-       }
-
-       if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)) {
-               func = filter_chat_handle_channel;
-       }
-       else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) {
-               func = filter_call_handle_channel;
-       }
-       else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST)) {
-               func = filter_contact_list_handle_channel;
-       }
-       else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES)) {
-               func = filter_tubes_handle_channel;
-       } else {
-               DEBUG ("Unknown channel type %s", channel_type);
-               return;
-       }
-
-       channel = tp_channel_new (connection, object_path, channel_type,
-                                 handle_type, handle, NULL);
-       tp_channel_run_until_ready (channel, NULL, NULL);
-
-       /* We abuse of suppress_handler, TRUE means OUTGOING */
-       func (EMPATHY_FILTER (filter), channel, suppress_handler);
-
-       g_object_unref (channel);
-}
-
-static void
-filter_connection_list_channels_cb (TpConnection    *connection,
-                                   const GPtrArray *channels,
-                                   const GError    *error,
-                                   gpointer         user_data,
-                                   GObject         *filter)
-{
-       guint i;
-
-       if (error) {
-               DEBUG ("Error listing channels: %s", error->message);
-               return;
-       }
-
-       g_object_set_data (G_OBJECT (connection), "had-channels",
-                          GUINT_TO_POINTER (1));
-
-       for (i = 0; i < channels->len; i++) {
-               GValueArray *values;
-
-               values = g_ptr_array_index (channels, i);
-               filter_conection_new_channel_cb (connection,
-                       g_value_get_boxed (g_value_array_get_nth (values, 0)),
-                       g_value_get_string (g_value_array_get_nth (values, 1)),
-                       g_value_get_uint (g_value_array_get_nth (values, 2)),
-                       g_value_get_uint (g_value_array_get_nth (values, 3)),
-                       TRUE, user_data, filter);
-       }
-}
-
-static void
-filter_connection_advertise_capabilities_cb (TpConnection    *connection,
-                                            const GPtrArray *capabilities,
-                                            const GError    *error,
-                                            gpointer         user_data,
-                                            GObject         *filter)
-{
-       if (error) {
-               DEBUG ("Error advertising capabilities: %s", error->message);
-       }
-}
-
-static void
-filter_connection_ready_cb (TpConnection  *connection,
-                           gpointer       unused,
-                           EmpathyFilter *filter)
-{
-       GPtrArray   *capabilities;
-       GType        cap_type;
-       GValue       cap = {0, };
-       const gchar *remove = NULL;
-
-       DEBUG ("Connection ready, accepting new channels");
-
-       tp_cli_connection_connect_to_new_channel (connection,
-                                                 filter_conection_new_channel_cb,
-                                                 NULL, NULL,
-                                                 G_OBJECT (filter), NULL);
-       tp_cli_connection_call_list_channels (connection, -1,
-                                             filter_connection_list_channels_cb,
-                                             NULL, NULL,
-                                             G_OBJECT (filter));
-
-       /* Advertise VoIP capabilities */
-       capabilities = g_ptr_array_sized_new (1);
-       cap_type = dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING,
-                                          G_TYPE_UINT, G_TYPE_INVALID);
-       g_value_init (&cap, cap_type);
-       g_value_take_boxed (&cap, dbus_g_type_specialized_construct (cap_type));
-       dbus_g_type_struct_set (&cap,
-                               0, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
-                               1, TP_CHANNEL_MEDIA_CAPABILITY_AUDIO |
-                                  TP_CHANNEL_MEDIA_CAPABILITY_VIDEO |
-                                  TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_STUN  |
-                                  TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_GTALK_P2P,
-                               G_MAXUINT);
-       g_ptr_array_add (capabilities, g_value_get_boxed (&cap));
-
-       tp_cli_connection_interface_capabilities_call_advertise_capabilities (
-               connection, -1,
-               capabilities, &remove,
-               filter_connection_advertise_capabilities_cb,
-               NULL, NULL, G_OBJECT (filter));
-}
-
-static void
-filter_update_account (EmpathyFilter *filter,
-                      McAccount     *account)
-{
-       EmpathyFilterPriv *priv = GET_PRIV (filter);
-       TpConnection      *connection;
-       gboolean           ready;
-
-       connection = g_hash_table_lookup (priv->accounts, account);
-       if (connection) {
-               return;
-       }
-
-       connection = mission_control_get_tpconnection (priv->mc, account, NULL);
-       if (!connection) {
-               return;
-       }
-
-       g_hash_table_insert (priv->accounts, g_object_ref (account), connection);
-       g_signal_connect (connection, "invalidated",
-                         G_CALLBACK (filter_connection_invalidated_cb),
-                         filter);
-
-       g_object_get (connection, "connection-ready", &ready, NULL);
-       if (ready) {
-               filter_connection_ready_cb (connection, NULL, filter);
-       } else {
-               g_signal_connect (connection, "notify::connection-ready",
-                                 G_CALLBACK (filter_connection_ready_cb),
-                                 filter);
-       }
-}
-
-static void
-filter_status_changed_cb (MissionControl           *mc,
-                         TpConnectionStatus        status,
-                         McPresence                presence,
-                         TpConnectionStatusReason  reason,
-                         const gchar              *unique_name,
-                         EmpathyFilter            *filter)
-{
-       McAccount *account;
-
-       account = mc_account_lookup (unique_name);
-       filter_update_account (filter, account);
-       g_object_unref (account);
-}
-
-static void
-filter_finalize (GObject *object)
-{
-       EmpathyFilterPriv *priv = GET_PRIV (object);
-
-       empathy_disconnect_account_status_changed (priv->token);
-       g_object_unref (priv->mc);
-
-       g_slist_foreach (priv->events, (GFunc) filter_event_free, NULL);
-       g_slist_free (priv->events);
-
-       g_hash_table_destroy (priv->accounts);
-       g_hash_table_destroy (priv->tubes);
-}
-
-static void
-filter_get_property (GObject    *object,
-                    guint       param_id,
-                    GValue     *value,
-                    GParamSpec *pspec)
-{
-       EmpathyFilterPriv *priv = GET_PRIV (object);
-
-       switch (param_id) {
-       case PROP_TOP_EVENT:
-               g_value_set_pointer (value, priv->events ? priv->events->data : NULL);
-               break;
-       default:
-               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
-               break;
-       };
-}
-
-static void
-empathy_filter_class_init (EmpathyFilterClass *klass)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-       object_class->finalize = filter_finalize;
-       object_class->get_property = filter_get_property;
-
-       g_object_class_install_property (object_class,
-                                        PROP_TOP_EVENT,
-                                        g_param_spec_pointer ("top-event",
-                                                              "The top event",
-                                                              "The first event in the events list",
-                                                              G_PARAM_READABLE));
-
-       g_type_class_add_private (object_class, sizeof (EmpathyFilterPriv));
-}
-
-static void
-empathy_filter_init (EmpathyFilter *filter)
-{
-       GList             *accounts, *l;
-       EmpathyFilterPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (filter,
-               EMPATHY_TYPE_FILTER, EmpathyFilterPriv);
-
-       filter->priv = priv;
-       priv->tubes = g_hash_table_new_full (filter_channel_hash,
-                                            filter_channel_equal,
-                                            g_object_unref, NULL);
-
-       priv->mc = empathy_mission_control_new ();
-       priv->token = empathy_connect_to_account_status_changed (priv->mc,
-               G_CALLBACK (filter_status_changed_cb),
-               filter, NULL);
-
-       priv->accounts = g_hash_table_new_full (empathy_account_hash,
-                                               empathy_account_equal,
-                                               g_object_unref,
-                                               g_object_unref);
-       accounts = mc_accounts_list_by_enabled (TRUE);
-       for (l = accounts; l; l = l->next) {
-               filter_update_account (filter, l->data);
-               g_object_unref (l->data);
-       }
-       g_list_free (accounts);
-}
-
-EmpathyFilter *
-empathy_filter_new (void)
-{
-       static EmpathyFilter *filter = NULL;
-
-       if (!filter) {
-               filter = g_object_new (EMPATHY_TYPE_FILTER, NULL);
-               g_object_add_weak_pointer (G_OBJECT (filter), (gpointer) &filter);
-       } else {
-               g_object_ref (filter);
-       }
-
-       return filter;
-}
-
diff --git a/src/empathy-filter.h b/src/empathy-filter.h
deleted file mode 100644 (file)
index 67b3238..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2007-2008 Collabora Ltd.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * Authors: Xavier Claessens <xclaesse@gmail.com>
- */
-
-#ifndef __EMPATHY_FILTER_H__
-#define __EMPATHY_FILTER_H__
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-
-#define EMPATHY_TYPE_FILTER         (empathy_filter_get_type ())
-#define EMPATHY_FILTER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_FILTER, EmpathyFilter))
-#define EMPATHY_FILTER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_FILTER, EmpathyFilterClass))
-#define EMPATHY_IS_FILTER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_FILTER))
-#define EMPATHY_IS_FILTER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_FILTER))
-#define EMPATHY_FILTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_FILTER, EmpathyFilterClass))
-
-typedef struct _EmpathyFilter      EmpathyFilter;
-typedef struct _EmpathyFilterClass EmpathyFilterClass;
-
-struct _EmpathyFilter {
-       GObject parent;
-       gpointer priv;
-};
-
-struct _EmpathyFilterClass {
-       GObjectClass parent_class;
-};
-
-typedef struct {
-       gchar *icon_name;
-       gchar *message;
-} EmpathyFilterEvent;
-
-GType               empathy_filter_get_type       (void) G_GNUC_CONST;
-EmpathyFilter *     empathy_filter_new            (void);
-void                empathy_filter_activate_event (EmpathyFilter      *filter,
-                                                  EmpathyFilterEvent *event);
-EmpathyFilterEvent *empathy_filter_get_top_event  (EmpathyFilter      *filter);
-
-G_END_DECLS
-
-#endif /* __EMPATHY_FILTER_H__ */
index bcb1f9f47cf66fdea74df93d6d9063a1c27c43fb..bd18b2d28318e2356f377caf32193dac9d468a8e 100644 (file)
 #include <glade/glade.h>
 #include <glib/gi18n.h>
 
+#include <telepathy-glib/util.h>
+
 #include <libempathy/empathy-utils.h>
 #include <libempathy/empathy-idle.h>
+#include <libempathy/empathy-contact-manager.h>
+#include <libempathy/empathy-dispatcher.h>
+#include <libempathy/empathy-tp-chat.h>
+#include <libempathy/empathy-tp-group.h>
 
 #include <libempathy-gtk/empathy-presence-chooser.h>
 #include <libempathy-gtk/empathy-conf.h>
 #include <libempathy-gtk/empathy-accounts-dialog.h>
 #include <libempathy-gtk/empathy-images.h>
 #include <libempathy-gtk/empathy-new-message-dialog.h>
+#include <libempathy-gtk/empathy-contact-dialogs.h>
 
 #include "empathy-status-icon.h"
 #include "empathy-preferences.h"
-#include "empathy-filter.h"
 
-#define DEBUG_FLAG EMPATHY_DEBUG_FILTER
+#define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
 #include <libempathy/empathy-debug.h>
 
 /* Number of ms to wait when blinking */
 #define BLINK_TIMEOUT 500
 
+typedef struct _StatusIconEvent StatusIconEvent;
+
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyStatusIcon)
 typedef struct {
        GtkStatusIcon      *icon;
        EmpathyIdle        *idle;
        MissionControl     *mc;
-       EmpathyFilter      *filter;
-       EmpathyFilterEvent *event;
+       EmpathyDispatcher  *dispatcher;
+       EmpathyContactManager *contact_manager;
+       GSList             *events;
        gboolean            showing_event_icon;
        guint               blink_timeout;
        gpointer            token;
@@ -65,8 +74,26 @@ typedef struct {
        GtkWidget          *status_item;
 } EmpathyStatusIconPriv;
 
+typedef void (*StatusIconEventFunc) (EmpathyStatusIcon *icon,
+                                    gpointer           user_data);
+
+struct _StatusIconEvent {
+       gchar               *icon_name;
+       gchar               *message;
+       StatusIconEventFunc  func;
+       gpointer             user_data;
+};
+
 G_DEFINE_TYPE (EmpathyStatusIcon, empathy_status_icon, G_TYPE_OBJECT);
 
+static void
+status_icon_event_free (StatusIconEvent *event)
+{
+       g_free (event->icon_name);
+       g_free (event->message);
+       g_slice_free (StatusIconEvent, event);
+}
+
 static void
 status_icon_set_visibility (EmpathyStatusIcon *icon,
                            gboolean           visible,
@@ -126,8 +153,8 @@ status_icon_update_tooltip (EmpathyStatusIcon *icon)
        EmpathyStatusIconPriv *priv = GET_PRIV (icon);
        const gchar           *tooltip = NULL;
 
-       if (priv->event) {
-               tooltip = priv->event->message;
+       if (priv->events) {
+               tooltip = ((StatusIconEvent*)priv->events->data)->message;
        }
 
        if (!tooltip) {
@@ -143,8 +170,8 @@ status_icon_update_icon (EmpathyStatusIcon *icon)
        EmpathyStatusIconPriv *priv = GET_PRIV (icon);
        const gchar           *icon_name;
 
-       if (priv->event && priv->showing_event_icon) {
-               icon_name = priv->event->icon_name;
+       if (priv->events && priv->showing_event_icon) {
+               icon_name = ((StatusIconEvent*)priv->events->data)->icon_name;
        } else {
                McPresence state;
 
@@ -177,15 +204,21 @@ status_icon_activate_cb (GtkStatusIcon     *status_icon,
 {
        EmpathyStatusIconPriv *priv = GET_PRIV (icon);
 
-       DEBUG ("Activated: %s", priv->event ? "event" : "toggle");
+       DEBUG ("Activated: %s", priv->events ? "event" : "toggle");
+
+       if (priv->events) {
+               StatusIconEvent *event;
 
-       if (priv->event) {
-               empathy_filter_activate_event (priv->filter, priv->event);
-               priv->event = empathy_filter_get_top_event (priv->filter);
+               event = priv->events->data;
+               if (event->func) {
+                       event->func (icon, event->user_data);
+               }
+               status_icon_event_free (event);
+               priv->events = g_slist_remove (priv->events, event);
                status_icon_update_tooltip (icon);
                status_icon_update_icon (icon);
 
-               if (!priv->event && priv->blink_timeout) {
+               if (!priv->events && priv->blink_timeout) {
                        g_source_remove (priv->blink_timeout);
                        priv->blink_timeout = 0;
                }
@@ -291,20 +324,225 @@ status_icon_blink_timeout_cb (EmpathyStatusIcon *icon)
 }
 
 static void
-status_icon_top_event_notify_cb (EmpathyStatusIcon *icon)
+status_icon_event_add (EmpathyStatusIcon   *icon,
+                      const gchar         *icon_name,
+                      const gchar         *message,
+                      StatusIconEventFunc  func,
+                      gpointer             user_data)
 {
        EmpathyStatusIconPriv *priv = GET_PRIV (icon);
+       StatusIconEvent       *event;
+       gboolean               had_events;
 
-       priv->event = empathy_filter_get_top_event (priv->filter);
-       priv->showing_event_icon = priv->event != NULL;
-       status_icon_update_icon (icon);
-       status_icon_update_tooltip (icon);
+       DEBUG ("Adding event: %s", message);
+
+       event = g_slice_new (StatusIconEvent);
+       event->icon_name = g_strdup (icon_name);
+       event->message = g_strdup (message);
+       event->func = func;
+       event->user_data = user_data;
+
+       had_events = (priv->events != NULL);
+       priv->events = g_slist_append (priv->events, event);
+       if (!had_events) {
+               priv->showing_event_icon = TRUE;
+               status_icon_update_icon (icon);
+               status_icon_update_tooltip (icon);
+
+               if (!priv->blink_timeout) {
+                       priv->blink_timeout = g_timeout_add (BLINK_TIMEOUT,
+                                                            (GSourceFunc) status_icon_blink_timeout_cb,
+                                                            icon);
+               }
+       }
+}
+
+static void
+status_icon_channel_process (EmpathyStatusIcon *icon,
+                            gpointer           user_data)
+{
+       EmpathyStatusIconPriv *priv = GET_PRIV (icon);
+       TpChannel             *channel = TP_CHANNEL (user_data);
+
+       empathy_dispatcher_channel_process (priv->dispatcher, channel);
+       g_object_unref (channel);
+}
+
+static void
+status_icon_chat_message_received_cb (EmpathyTpChat     *tp_chat,
+                                     EmpathyMessage    *message,
+                                     EmpathyStatusIcon *icon)
+{
+       EmpathyContact  *sender;
+       gchar           *msg;
+       TpChannel       *channel;
+
+       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));
+
+       channel = empathy_tp_chat_get_channel (tp_chat);
+       status_icon_event_add (icon, EMPATHY_IMAGE_NEW_MESSAGE, msg,
+                              status_icon_channel_process,
+                              g_object_ref (channel));
+
+       g_free (msg);
+       g_object_unref (tp_chat);
+}
+
+static void
+status_icon_filter_channel_cb (EmpathyDispatcher *dispatcher,
+                              TpChannel         *channel,
+                              EmpathyStatusIcon *icon)
+{
+       gchar *channel_type;
+
+       g_object_get (channel, "channel-type", &channel_type, NULL);
+       if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)) {
+               EmpathyTpChat *tp_chat;
+
+               tp_chat = empathy_tp_chat_new (channel);
+               g_signal_connect (tp_chat, "message-received",
+                                 G_CALLBACK (status_icon_chat_message_received_cb),
+                                 icon);
+       }
+       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));
+
+               status_icon_event_add (icon, EMPATHY_IMAGE_VOIP, msg,
+                                      status_icon_channel_process,
+                                      g_object_ref (channel));
+
+               g_free (msg);
+               g_object_unref (contact);
+               g_object_unref (tp_group);
+       }
+
+       g_free (channel_type);
+}
+
+static void
+status_icon_tube_process (EmpathyStatusIcon *icon,
+                         gpointer           user_data)
+{
+       EmpathyStatusIconPriv *priv = GET_PRIV (icon);
+       EmpathyDispatcherTube *tube = (EmpathyDispatcherTube*) user_data;
 
-       if (!priv->blink_timeout) {
-               priv->blink_timeout = g_timeout_add (BLINK_TIMEOUT,
-                                                    (GSourceFunc) status_icon_blink_timeout_cb,
-                                                    icon);
+       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 (_("%s offered you an invitation, but "
+                                        "you don't have the needed external "
+                                        "application to handle it."),
+                                      empathy_contact_get_name (tube->initiator));
+
+               dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
+                                                GTK_MESSAGE_ERROR,
+                                                GTK_BUTTONS_OK, 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);
        }
+
+       empathy_dispatcher_tube_unref (tube);
+}
+
+static void
+status_icon_filter_tube_cb (EmpathyDispatcher     *dispatcher,
+                           EmpathyDispatcherTube *tube,
+                           EmpathyStatusIcon     *icon)
+{
+       const gchar *icon_name;
+       gchar       *msg;
+
+       empathy_contact_run_until_ready (tube->initiator,
+                                        EMPATHY_CONTACT_READY_NAME, NULL);
+
+       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 (_("%s is offering you an invitation, but "
+                                        "you don't have the needed external "
+                                        "application to handle it."),
+                                      empathy_contact_get_name (tube->initiator));
+       }
+
+       status_icon_event_add (icon, icon_name, msg, status_icon_tube_process,
+                              empathy_dispatcher_tube_ref (tube));
+
+       g_free (msg);
+}
+
+static void
+status_icon_pending_subscribe (EmpathyStatusIcon *icon,
+                              gpointer           user_data)
+{
+       EmpathyContact *contact = EMPATHY_CONTACT (user_data);
+
+       empathy_subscription_dialog_show (contact, NULL);
+       g_object_unref (contact);
+}
+
+static void
+status_icon_pendings_changed_cb (EmpathyContactList *list,
+                                EmpathyContact     *contact,
+                                EmpathyContact     *actor,
+                                guint               reason,
+                                gchar              *message,
+                                gboolean            is_pending,
+                                EmpathyStatusIcon  *icon)
+{
+       GString *str;
+
+       if (!is_pending) {
+               /* FIXME: remove event if any */
+               return;
+       }
+
+       DEBUG ("New local pending contact");
+
+       empathy_contact_run_until_ready (contact,
+                                        EMPATHY_CONTACT_READY_NAME,
+                                        NULL);
+
+       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);
+       }
+
+       status_icon_event_add (icon, GTK_STOCK_DIALOG_QUESTION, str->str,
+                              status_icon_pending_subscribe,
+                              g_object_ref (contact));
+
+       g_string_free (str, TRUE);
 }
 
 static void
@@ -317,11 +555,13 @@ status_icon_finalize (GObject *object)
        }
 
        empathy_disconnect_account_status_changed (priv->token);
+       g_slist_foreach (priv->events, (GFunc) status_icon_event_free, NULL);
+       g_slist_free (priv->events);
 
        g_object_unref (priv->icon);
        g_object_unref (priv->idle);
-       g_object_unref (priv->filter);
        g_object_unref (priv->mc);
+       g_object_unref (priv->contact_manager);
 }
 
 static void
@@ -342,11 +582,9 @@ status_icon_status_changed_cb (MissionControl           *mc,
                               const gchar              *unique_name,
                               EmpathyStatusIcon        *icon)
 {
+       EmpathyStatusIconPriv *priv = GET_PRIV (icon);
        GList                 *accounts, *l;
        guint                  connection_status = 1;
-       EmpathyStatusIconPriv *priv;
-
-       priv = GET_PRIV (icon);
 
        /* Check for a connected account */
        accounts = mc_accounts_list_by_enabled (TRUE);
@@ -354,8 +592,9 @@ status_icon_status_changed_cb (MissionControl           *mc,
                connection_status = mission_control_get_connection_status (priv->mc,
                                                                           l->data,
                                                                           NULL);
-               if (connection_status == 0)
+               if (connection_status == 0) {
                        break;
+               }
        }
        mc_accounts_list_free (accounts);
 
@@ -372,7 +611,8 @@ empathy_status_icon_init (EmpathyStatusIcon *icon)
        priv->icon = gtk_status_icon_new ();
        priv->mc = empathy_mission_control_new ();
        priv->idle = empathy_idle_new ();
-       priv->filter = empathy_filter_new ();
+       priv->dispatcher = empathy_dispatcher_new ();
+       priv->contact_manager = empathy_contact_manager_new ();
        priv->token = empathy_connect_to_account_status_changed (priv->mc,
                        G_CALLBACK (status_icon_status_changed_cb),
                        icon, NULL);
@@ -389,9 +629,15 @@ empathy_status_icon_init (EmpathyStatusIcon *icon)
        g_signal_connect_swapped (priv->idle, "notify",
                                  G_CALLBACK (status_icon_idle_notify_cb),
                                  icon);
-       g_signal_connect_swapped (priv->filter, "notify::top-event",
-                                 G_CALLBACK (status_icon_top_event_notify_cb),
-                                 icon);
+       g_signal_connect (priv->dispatcher, "filter-channel",
+                         G_CALLBACK (status_icon_filter_channel_cb),
+                         icon);
+       g_signal_connect (priv->dispatcher, "filter-tube",
+                         G_CALLBACK (status_icon_filter_tube_cb),
+                         icon);
+       g_signal_connect (priv->contact_manager, "pendings-changed",
+                         G_CALLBACK (status_icon_pendings_changed_cb),
+                         icon);
        g_signal_connect (priv->icon, "activate",
                          G_CALLBACK (status_icon_activate_cb),
                          icon);
index f28eae9599d7497141962ea735eeecdb758e79eb..242f946a1c568544aaecd185338546828a16371e 100644 (file)
 
 #include <libempathy/empathy-idle.h>
 #include <libempathy/empathy-utils.h>
+#include <libempathy/empathy-dispatcher.h>
+#include <libempathy/empathy-tp-chat.h>
+#include <libempathy/empathy-tp-call.h>
 
 #include <libempathy-gtk/empathy-conf.h>
 
 #include "empathy-main-window.h"
 #include "empathy-status-icon.h"
+#include "empathy-call-window.h"
+#include "empathy-chat-window.h"
 #include "bacon-message-connection.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
 
 static BaconMessageConnection *connection = NULL;
 
+static void
+dispatch_channel_cb (EmpathyDispatcher *dispatcher,
+                    TpChannel         *channel,
+                    gpointer           user_data)
+{
+       gchar *channel_type;
+
+       g_object_get (channel, "channel-type", &channel_type, NULL);
+       if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)) {
+               EmpathyTpChat *tp_chat;
+               EmpathyChat   *chat = NULL;
+               const gchar   *id;
+
+               tp_chat = empathy_tp_chat_new (channel);
+               empathy_run_until_ready (tp_chat);
+
+               id = empathy_tp_chat_get_id (tp_chat);
+               if (!id) {
+                       EmpathyContact *contact;
+
+                       contact = empathy_tp_chat_get_remote_contact (tp_chat);
+                       if (contact) {
+                               id = empathy_contact_get_id (contact);
+                       }
+               }
+
+               if (id) {
+                       McAccount *account;
+
+                       account = empathy_tp_chat_get_account (tp_chat);
+                       chat = empathy_chat_window_find_chat (account, id);
+               }
+
+               if (chat) {
+                       empathy_chat_set_tp_chat (chat, tp_chat);
+               } else {
+                       chat = empathy_chat_new (tp_chat);
+               }
+
+               empathy_chat_window_present_chat (chat);
+               g_object_unref (tp_chat);
+       }
+       else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) {
+               EmpathyTpCall *tp_call;
+
+               tp_call = empathy_tp_call_new (channel);
+               empathy_call_window_new (tp_call);
+               g_object_unref (tp_call);
+       }
+}
+
 static void
 service_ended_cb (MissionControl *mc,
                  gpointer        user_data)
@@ -300,6 +356,7 @@ main (int argc, char *argv[])
 {
        guint32            startup_timestamp;
        EmpathyStatusIcon *icon;
+       EmpathyDispatcher *dispatcher;
        GtkWidget         *window;
        MissionControl    *mc;
        EmpathyIdle       *idle;
@@ -402,6 +459,12 @@ main (int argc, char *argv[])
                                                       window);
        }
 
+       /* Handle channels */
+       dispatcher = empathy_dispatcher_new ();
+       g_signal_connect (dispatcher, "dispatch-channel",
+                         G_CALLBACK (dispatch_channel_cb),
+                         NULL);
+
        gtk_main ();
 
        empathy_idle_set_state (idle, MC_PRESENCE_OFFLINE);
@@ -409,6 +472,7 @@ main (int argc, char *argv[])
        g_object_unref (mc);
        g_object_unref (idle);
        g_object_unref (icon);
+       g_object_unref (dispatcher);
 
        return EXIT_SUCCESS;
 }