]> git.0d.be Git - empathy.git/blobdiff - src/empathy.c
Merge remote-tracking branch 'glassrose/add-All-service-selection-in-debug-window'
[empathy.git] / src / empathy.c
index f8479142316c8b30f99dd2bf3a0ca3a2e9ff9e3b..f59a54f17f7c559ec41512f87077c5a5b95e5ee4 100644 (file)
 #include <string.h>
 
 #include <glib.h>
+#include <glib/gstdio.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
-#include <unique/unique.h>
 
-#if HAVE_LIBCHAMPLAIN
+#ifdef HAVE_LIBCHAMPLAIN
 #include <clutter-gtk/clutter-gtk.h>
 #endif
 
 #include <telepathy-glib/connection-manager.h>
 #include <telepathy-glib/interfaces.h>
 
-#ifdef ENABLE_TPL
+#include <telepathy-yell/telepathy-yell.h>
+
 #include <telepathy-logger/log-manager.h>
-#include <telepathy-logger/log-store-empathy.h>
-#else
 
-#include <libempathy/empathy-log-manager.h>
-#endif /* ENABLE_TPL */
-#include <libempathy/empathy-idle.h>
+#include <libempathy/empathy-client-factory.h>
+#include <libempathy/empathy-connection-aggregator.h>
+#include <libempathy/empathy-presence-manager.h>
 #include <libempathy/empathy-utils.h>
-#include <libempathy/empathy-call-factory.h>
 #include <libempathy/empathy-chatroom-manager.h>
 #include <libempathy/empathy-account-settings.h>
 #include <libempathy/empathy-connectivity.h>
 #include <libempathy/empathy-connection-managers.h>
-#include <libempathy/empathy-dispatcher.h>
-#include <libempathy/empathy-dispatch-operation.h>
+#include <libempathy/empathy-request-util.h>
 #include <libempathy/empathy-ft-factory.h>
+#include <libempathy/empathy-gsettings.h>
 #include <libempathy/empathy-tp-chat.h>
-#include <libempathy/empathy-tp-call.h>
 
-#include <libempathy-gtk/empathy-conf.h>
 #include <libempathy-gtk/empathy-ui-utils.h>
 #include <libempathy-gtk/empathy-location-manager.h>
+#include <libempathy-gtk/empathy-notify-manager.h>
 
-#include "empathy-main-window.h"
-#include "empathy-import-mc4-accounts.h"
+#include "empathy-roster-window.h"
 #include "empathy-accounts-common.h"
 #include "empathy-accounts-dialog.h"
 #include "empathy-status-icon.h"
-#include "empathy-call-window.h"
-#include "empathy-chat-window.h"
 #include "empathy-ft-manager.h"
+#include "empathy-notifications-approver.h"
 
 #include "extensions/extensions.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
 #include <libempathy/empathy-debug.h>
 
-#include <gst/gst.h>
+#define EMPATHY_DBUS_NAME "org.gnome.Empathy"
+
+#define EMPATHY_TYPE_APP (empathy_app_get_type ())
+#define EMPATHY_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EMPATHY_TYPE_APP, EmpathyApp))
+#define EMPATHY_APP_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EMPATHY_TYPE_APP, EmpathyAppClass))
+#define EMPATHY_IS_EMPATHY_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EMPATHY_TYPE_APP))
+#define EMPATHY_IS_EMPATHY_APP_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EMPATHY_TYPE_APP))
+#define EMPATHY_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_APP, EmpathyAppClass))
+
+typedef struct _EmpathyApp EmpathyApp;
+typedef struct _EmpathyAppClass EmpathyAppClass;
+
+enum
+{
+  PROP_NO_CONNECT = 1,
+  PROP_START_HIDDEN
+};
+
+GType empathy_app_get_type (void);
+
+struct _EmpathyAppClass
+{
+  GtkApplicationClass parent_class;
+};
+
+struct _EmpathyApp
+{
+  GtkApplication parent;
+
+  /* Properties */
+  gboolean no_connect;
+  gboolean start_hidden;
+  gboolean show_preferences;
+  gchar *preferences_tab;
 
-static gboolean start_hidden = FALSE;
-static gboolean no_connect = FALSE;
+  gboolean activated;
+
+  GtkWidget *window;
+  EmpathyStatusIcon *icon;
+  TpAccountManager *account_manager;
+  TplLogManager *log_manager;
+  EmpathyChatroomManager *chatroom_manager;
+  EmpathyFTFactory  *ft_factory;
+  EmpathyPresenceManager *presence_mgr;
+  EmpathyConnectivity *connectivity;
+  GSettings *gsettings;
+  EmpathyNotificationsApprover *notifications_approver;
+  EmpathyConnectionAggregator *conn_aggregator;
+#ifdef HAVE_GEOCLUE
+  EmpathyLocationManager *location_manager;
+#endif
+#ifdef ENABLE_DEBUG
+  TpDebugSender *debug_sender;
+#endif
+
+  gboolean shell_running;
+};
+
+
+G_DEFINE_TYPE(EmpathyApp, empathy_app, GTK_TYPE_APPLICATION)
+
+static void
+empathy_app_dispose (GObject *object)
+{
+  EmpathyApp *self = EMPATHY_APP (object);
+  void (*dispose) (GObject *) =
+    G_OBJECT_CLASS (empathy_app_parent_class)->dispose;
+
+  /* Only set our presence to offline when exiting if GNOME Shell is not
+   * running */
+  if (self->presence_mgr != NULL &&
+      !self->shell_running)
+    {
+      empathy_presence_manager_set_state (self->presence_mgr,
+          TP_CONNECTION_PRESENCE_TYPE_OFFLINE);
+    }
+
+#ifdef ENABLE_DEBUG
+  tp_clear_object (&self->debug_sender);
+#endif
+
+  tp_clear_object (&self->presence_mgr);
+  tp_clear_object (&self->connectivity);
+  tp_clear_object (&self->icon);
+  tp_clear_object (&self->account_manager);
+  tp_clear_object (&self->log_manager);
+  tp_clear_object (&self->chatroom_manager);
+#ifdef HAVE_GEOCLUE
+  tp_clear_object (&self->location_manager);
+#endif
+  tp_clear_object (&self->ft_factory);
+  tp_clear_object (&self->gsettings);
+  tp_clear_object (&self->notifications_approver);
+  tp_clear_object (&self->conn_aggregator);
+
+  if (dispose != NULL)
+    dispose (object);
+}
+
+static void
+empathy_app_finalize (GObject *object)
+{
+  EmpathyApp *self = EMPATHY_APP (object);
+  void (*finalize) (GObject *) =
+    G_OBJECT_CLASS (empathy_app_parent_class)->finalize;
+
+  g_free (self->preferences_tab);
+
+  if (finalize != NULL)
+    finalize (object);
+}
 
 static void account_manager_ready_cb (GObject *source_object,
     GAsyncResult *result,
     gpointer user_data);
 
 static void
-dispatch_cb (EmpathyDispatcher *dispatcher,
-    EmpathyDispatchOperation *operation,
+empathy_app_set_property (GObject *object,
+    guint prop_id,
+    const GValue *value,
+    GParamSpec *pspec)
+{
+  EmpathyApp *self = EMPATHY_APP (object);
+
+  switch (prop_id)
+    {
+      case PROP_NO_CONNECT:
+        self->no_connect = g_value_get_boolean (value);
+        break;
+      case PROP_START_HIDDEN:
+        self->start_hidden = g_value_get_boolean (value);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+new_incoming_transfer_cb (EmpathyFTFactory *factory,
+    EmpathyFTHandler *handler,
+    GError *error,
+    gpointer user_data)
+{
+  if (error)
+    empathy_ft_manager_display_error (handler, error);
+  else
+    empathy_receive_file_with_file_chooser (handler);
+}
+
+static void
+new_ft_handler_cb (EmpathyFTFactory *factory,
+    EmpathyFTHandler *handler,
+    GError *error,
     gpointer user_data)
 {
-  GQuark channel_type;
+  if (error)
+    empathy_ft_manager_display_error (handler, error);
+  else
+    empathy_ft_manager_add_handler (handler);
 
-  channel_type = empathy_dispatch_operation_get_channel_type_id (operation);
+  g_object_unref (handler);
+}
 
-  if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
-    {
-      EmpathyTpChat *tp_chat;
-      EmpathyChat   *chat = NULL;
-      const gchar   *id;
+static gboolean
+empathy_app_local_command_line (GApplication *app,
+    gchar ***arguments,
+    gint *exit_status);
 
-      tp_chat = EMPATHY_TP_CHAT
-        (empathy_dispatch_operation_get_channel_wrapper (operation));
+static void
+empathy_presence_manager_set_auto_away_cb (GSettings *gsettings,
+    const gchar *key,
+    gpointer user_data)
+{
+  EmpathyPresenceManager *presence_mgr = user_data;
 
-      id = empathy_tp_chat_get_id (tp_chat);
-      if (!EMP_STR_EMPTY (id))
-        {
-          TpConnection *connection;
-          TpAccount *account;
+  empathy_presence_manager_set_auto_away (presence_mgr,
+      g_settings_get_boolean (gsettings, key));
+}
 
-          connection = empathy_tp_chat_get_connection (tp_chat);
-          account = empathy_get_account_for_connection (connection);
-          chat = empathy_chat_window_find_chat (account, id);
-        }
+#define GNOME_SHELL_BUS_NAME "org.gnome.Shell"
 
-      if (chat)
-        {
-          empathy_chat_set_tp_chat (chat, tp_chat);
-        }
-      else
+static void
+list_names_cb (TpDBusDaemon *bus_daemon,
+        const gchar * const *names,
+        const GError *error,
+        gpointer user_data,
+        GObject *weak_object)
+{
+  EmpathyApp *self = (EmpathyApp *) weak_object;
+  guint i;
+
+  if (error != NULL)
+      goto out;
+
+  for (i = 0; names[i] != NULL; i++)
+    {
+      if (!tp_strdiff (names[i], GNOME_SHELL_BUS_NAME))
         {
-          chat = empathy_chat_new (tp_chat);
-          /* empathy_chat_new returns a floating reference as EmpathyChat is
-           * a GtkWidget. This reference will be taken by a container
-           * (a GtkNotebook) when we'll call empathy_chat_window_present_chat */
+          self->shell_running = TRUE;
+          break;
         }
+    }
 
-      empathy_chat_window_present_chat (chat);
+out:
+  if (self->shell_running)
+    {
+      DEBUG ("GNOME Shell is running, don't create status icon");
 
-      empathy_dispatch_operation_claim (operation);
+      /* Rely on GNOME Shell to watch session state */
+      empathy_presence_manager_set_auto_away (self->presence_mgr, FALSE);
+
+      empathy_roster_window_set_shell_running (
+          EMPATHY_ROSTER_WINDOW (self->window), TRUE);
     }
-  else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
+  else
     {
-      EmpathyCallFactory *factory;
+      gboolean autoaway;
+
+      self->icon = empathy_status_icon_new (GTK_WINDOW (self->window),
+          self->start_hidden);
+
+      /* Allow Empathy to watch session state */
+      autoaway = g_settings_get_boolean (self->gsettings,
+          EMPATHY_PREFS_AUTOAWAY);
+
+      g_signal_connect (self->gsettings,
+          "changed::" EMPATHY_PREFS_AUTOAWAY,
+          G_CALLBACK (empathy_presence_manager_set_auto_away_cb),
+          self->presence_mgr);
 
-      factory = empathy_call_factory_get ();
-      empathy_call_factory_claim_channel (factory, operation);
+      empathy_presence_manager_set_auto_away (self->presence_mgr, autoaway);
     }
-  else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER)
+}
+
+static int
+empathy_app_command_line (GApplication *app,
+    GApplicationCommandLine *cmdline)
+{
+  EmpathyApp *self = (EmpathyApp *) app;
+  gchar **args, **argv;
+  gint argc, exit_status, i;
+
+  args = g_application_command_line_get_arguments (cmdline, &argc);
+  /* We have to make an extra copy of the array, since g_option_context_parse()
+   * assumes that it can remove strings from the array without freeing them. */
+  argv = g_new (gchar*, argc + 1);
+  for (i = 0; i <= argc; i++)
+    argv[i] = args[i];
+
+  if (empathy_app_local_command_line (app, &argv, &exit_status))
+    DEBUG ("failed to parse command line!");
+
+  g_free (argv);
+  g_strfreev (args);
+
+  if (!self->activated)
     {
-      EmpathyFTFactory *factory;
+      GError *error = NULL;
+      TpDBusDaemon *dbus;
+
+      /* Create the FT factory */
+      self->ft_factory = empathy_ft_factory_dup_singleton ();
+      g_signal_connect (self->ft_factory, "new-ft-handler",
+          G_CALLBACK (new_ft_handler_cb), NULL);
+      g_signal_connect (self->ft_factory, "new-incoming-transfer",
+          G_CALLBACK (new_incoming_transfer_cb), NULL);
+
+      if (!empathy_ft_factory_register (self->ft_factory, &error))
+        {
+          g_warning ("Failed to register FileTransfer handler: %s",
+              error->message);
+          g_error_free (error);
+        }
+
+      self->activated = TRUE;
+
+      /* Setting up UI */
+      self->window = empathy_roster_window_dup ();
+
+      gtk_application_add_window (GTK_APPLICATION (app),
+          GTK_WINDOW (self->window));
+
+      /* check if Shell is running */
+      dbus = tp_dbus_daemon_dup (&error);
+      g_assert_no_error (error);
+
+      tp_dbus_daemon_list_names (dbus, -1, list_names_cb,
+              self, NULL, G_OBJECT (self));
 
-      factory = empathy_ft_factory_dup_singleton ();
+      g_object_unref (dbus);
 
-      /* if the operation is not incoming, don't claim it,
-       * as it might have been triggered by another client, and
-       * we are observing it.
+      self->notifications_approver =
+        empathy_notifications_approver_dup_singleton ();
+    }
+  else
+    {
+      /* We're requested to show stuff again, disable the start hidden global in
+       * case the accounts wizard wants to pop up.
        */
-      if (empathy_dispatch_operation_is_incoming (operation))
-        empathy_ft_factory_claim_channel (factory, operation);
+      self->start_hidden = FALSE;
     }
+
+  if (self->show_preferences)
+    empathy_roster_window_show_preferences (
+        EMPATHY_ROSTER_WINDOW (self->window), self->preferences_tab);
+
+  if (!self->start_hidden)
+    empathy_window_present (GTK_WINDOW (self->window));
+
+  /* Display the accounts dialog if needed */
+  tp_proxy_prepare_async (self->account_manager, NULL,
+      account_manager_ready_cb, self);
+
+  return 0;
+}
+
+static gboolean
+preferences_cb (const char *option_name,
+    const char *value,
+    gpointer data,
+    GError **error)
+{
+  EmpathyApp *self = data;
+
+  self->show_preferences = TRUE;
+
+  g_free (self->preferences_tab);
+  self->preferences_tab = g_strdup (value);
+
+  return TRUE;
+}
+
+static gboolean
+show_version_cb (const char *option_name,
+    const char *value,
+    gpointer data,
+    GError **error);
+
+static gboolean
+empathy_app_local_command_line (GApplication *app,
+    gchar ***arguments,
+    gint *exit_status)
+{
+  EmpathyApp *self = (EmpathyApp *) app;
+  gint i;
+  gchar **argv;
+  gint argc = 0;
+  gboolean retval = FALSE;
+  GError *error = NULL;
+  gboolean no_connect = FALSE, start_hidden = FALSE;
+
+  GOptionContext *optcontext;
+  GOptionGroup *group;
+  GOptionEntry options[] = {
+      { "no-connect", 'n',
+        0, G_OPTION_ARG_NONE, &no_connect,
+        N_("Don't connect on startup"),
+        NULL },
+      { "start-hidden", 'h',
+        0, G_OPTION_ARG_NONE, &start_hidden,
+        N_("Don't display the contact list or any other dialogs on startup"),
+        NULL },
+      { "show-preferences", 'p',
+        G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, &preferences_cb,
+        NULL, NULL },
+      { "version", 'v',
+        G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_version_cb,
+        NULL, NULL },
+      { NULL }
+  };
+
+  /* We create a group so that GOptionArgFuncs get the user data */
+  group = g_option_group_new ("empathy", NULL, NULL, app, NULL);
+  g_option_group_add_entries (group, options);
+
+  optcontext = g_option_context_new (N_("- Empathy IM Client"));
+  g_option_context_add_group (optcontext, gtk_get_option_group (TRUE));
+  g_option_context_set_main_group (optcontext, group);
+  g_option_context_set_translation_domain (optcontext, GETTEXT_PACKAGE);
+
+  argc = g_strv_length (*arguments);
+
+  /* We dup the args because g_option_context_parse() sets things to NULL,
+   * but we want to parse all the command line to the primary instance
+   * if necessary. */
+  argv = g_new (gchar*, argc + 1);
+  for (i = 0; i <= argc; i++)
+    argv[i] = (*arguments)[i];
+
+  if (!g_option_context_parse (optcontext, &argc, &argv, &error))
+    {
+      g_print ("%s\nRun '%s --help' to see a full list of available command "
+          "line options.\n",
+          error->message, argv[0]);
+      g_warning ("Error in empathy init: %s", error->message);
+
+      *exit_status = EXIT_FAILURE;
+      retval = TRUE;
+    }
+
+  g_free (argv);
+
+  g_option_context_free (optcontext);
+
+  self->no_connect = no_connect;
+  self->start_hidden = start_hidden;
+
+  return retval;
+}
+
+static void empathy_app_constructed (GObject *object);
+
+static void
+empathy_app_class_init (EmpathyAppClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GApplicationClass *g_app_class = G_APPLICATION_CLASS (klass);
+  GParamSpec *spec;
+
+  gobject_class->set_property = empathy_app_set_property;
+  gobject_class->constructed = empathy_app_constructed;
+  gobject_class->dispose = empathy_app_dispose;
+  gobject_class->finalize = empathy_app_finalize;
+
+  g_app_class->command_line = empathy_app_command_line;
+  g_app_class->local_command_line = empathy_app_local_command_line;
+
+  spec = g_param_spec_boolean ("no-connect", "no connect",
+      "Don't connect on startup",
+      FALSE,
+      G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
+  g_object_class_install_property (gobject_class, PROP_NO_CONNECT, spec);
+
+  spec = g_param_spec_boolean ("start-hidden", "start hidden",
+      "Don't display the contact list or any other dialogs on startup",
+      FALSE,
+      G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
+  g_object_class_install_property (gobject_class, PROP_START_HIDDEN, spec);
+}
+
+static void
+empathy_app_init (EmpathyApp *self)
+{
 }
 
 static void
-use_conn_notify_cb (EmpathyConf *conf,
+use_conn_notify_cb (GSettings *gsettings,
     const gchar *key,
     gpointer     user_data)
 {
   EmpathyConnectivity *connectivity = user_data;
-  gboolean     use_conn;
 
-  if (empathy_conf_get_bool (conf, key, &use_conn))
-    {
-      empathy_connectivity_set_use_conn (connectivity, use_conn);
-    }
+  empathy_connectivity_set_use_conn (connectivity,
+      g_settings_get_boolean (gsettings, key));
 }
 
 static void
@@ -241,59 +603,12 @@ migrate_config_to_xdg_dir (void)
 }
 
 static void
-accounts_application_exited_cb (GPid pid,
-    gint status,
-    gpointer data)
-{
-  if (status)
-    {
-      g_warning ("accounts application exited with status %d: '%s'",
-          status, g_strerror (status));
-    }
-}
-
-static void
-show_accounts_ui (GdkScreen *screen,
+show_accounts_ui (EmpathyApp *self,
+    GdkScreen *screen,
     gboolean if_needed)
 {
   empathy_accounts_dialog_show_application (screen,
-      accounts_application_exited_cb, NULL, NULL, if_needed, start_hidden);
-}
-
-static UniqueResponse
-unique_app_message_cb (UniqueApp *unique_app,
-    gint command,
-    UniqueMessageData *data,
-    guint timestamp,
-    gpointer user_data)
-{
-  GtkWindow *window = user_data;
-  TpAccountManager *account_manager;
-
-  DEBUG ("Other instance launched, presenting the main window. "
-      "Command=%d, timestamp %u", command, timestamp);
-
-  /* XXX: the standalone app somewhat breaks this case, since
-   * communicating it would be a pain */
-
-  /* We're requested to show stuff again, disable the start hidden global
-   * in case the accounts wizard wants to pop up.
-   */
-  start_hidden = FALSE;
-
-  gtk_window_set_screen (GTK_WINDOW (window),
-      unique_message_data_get_screen (data));
-  gtk_window_set_startup_id (GTK_WINDOW (window),
-      unique_message_data_get_startup_id (data));
-  gtk_window_present_with_time (GTK_WINDOW (window), timestamp);
-  gtk_window_set_skip_taskbar_hint (window, FALSE);
-
-  account_manager = tp_account_manager_dup ();
-  tp_account_manager_prepare_async (account_manager, NULL,
-      account_manager_ready_cb, NULL);
-  g_object_unref (account_manager);
-
-  return UNIQUE_RESPONSE_OK;
+      NULL, if_needed, self->start_hidden);
 }
 
 static gboolean
@@ -305,46 +620,6 @@ show_version_cb (const char *option_name,
   g_print ("%s\n", PACKAGE_STRING);
 
   exit (EXIT_SUCCESS);
-
-  return FALSE;
-}
-
-static void
-new_incoming_transfer_cb (EmpathyFTFactory *factory,
-    EmpathyFTHandler *handler,
-    GError *error,
-    gpointer user_data)
-{
-  if (error)
-    empathy_ft_manager_display_error (handler, error);
-  else
-    empathy_receive_file_with_file_chooser (handler);
-}
-
-static void
-new_ft_handler_cb (EmpathyFTFactory *factory,
-    EmpathyFTHandler *handler,
-    GError *error,
-    gpointer user_data)
-{
-  if (error)
-    empathy_ft_manager_display_error (handler, error);
-  else
-    empathy_ft_manager_add_handler (handler);
-
-  g_object_unref (handler);
-}
-
-static void
-new_call_handler_cb (EmpathyCallFactory *factory,
-    EmpathyCallHandler *handler,
-    gboolean outgoing,
-    gpointer user_data)
-{
-  EmpathyCallWindow *window;
-
-  window = empathy_call_window_new (handler);
-  gtk_widget_show (GTK_WIDGET (window));
 }
 
 static void
@@ -353,146 +628,83 @@ account_manager_ready_cb (GObject *source_object,
     gpointer user_data)
 {
   TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
+  EmpathyApp *self = user_data;
   GError *error = NULL;
-  EmpathyIdle *idle;
-  EmpathyConnectivity *connectivity;
-  gboolean autoconnect = TRUE;
   TpConnectionPresenceType presence;
 
-  if (!tp_account_manager_prepare_finish (manager, result, &error))
+  if (!tp_proxy_prepare_finish (manager, result, &error))
     {
+      GtkWidget *dialog;
+
       DEBUG ("Failed to prepare account manager: %s", error->message);
+
+      dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
+          GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+          _("Error contacting the Account Manager"));
+      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+          _("There was an error while trying to connect to the Telepathy "
+            "Account Manager. The error was:\n\n%s"),
+          error->message);
+
+      gtk_dialog_run (GTK_DIALOG (dialog));
+      gtk_widget_destroy (dialog);
+
       g_error_free (error);
       return;
     }
 
   /* Autoconnect */
-  idle = empathy_idle_dup_singleton ();
-  connectivity = empathy_connectivity_dup_singleton ();
-
   presence = tp_account_manager_get_most_available_presence (manager, NULL,
       NULL);
 
-  empathy_conf_get_bool (empathy_conf_get (),
-      EMPATHY_PREFS_AUTOCONNECT, &autoconnect);
-  if (autoconnect && !no_connect &&
+  if (g_settings_get_boolean (self->gsettings, EMPATHY_PREFS_AUTOCONNECT) &&
+      !self->no_connect &&
       tp_connection_presence_type_cmp_availability
           (presence, TP_CONNECTION_PRESENCE_TYPE_OFFLINE)
             <= 0)
       /* if current state is Offline, then put it online */
-      empathy_idle_set_state (idle, TP_CONNECTION_PRESENCE_TYPE_AVAILABLE);
+      empathy_presence_manager_set_state (self->presence_mgr,
+          TP_CONNECTION_PRESENCE_TYPE_AVAILABLE);
 
-  /* Pop up the accounts dialog if it's needed (either when we don't have any
-   * accounts yet or when we haven't imported mc4 accounts yet */
-  if (!empathy_accounts_has_accounts (manager)
-      || !empathy_import_mc4_has_imported ())
-    show_accounts_ui (gdk_screen_get_default (), TRUE);
-
-  g_object_unref (idle);
-  g_object_unref (connectivity);
+  /* Pop up the accounts dialog if we don't have any account */
+  if (!empathy_accounts_has_accounts (manager))
+    show_accounts_ui (self, gdk_screen_get_default (), TRUE);
 }
 
-static EmpathyDispatcher *
-setup_dispatcher (void)
+static void
+account_join_chatrooms (TpAccount *account,
+  EmpathyChatroomManager *chatroom_manager)
 {
-  EmpathyDispatcher *d;
-  GPtrArray *filters;
-  struct {
-    const gchar *channeltype;
-    TpHandleType handletype;
-  } types[] = {
-    /* Text channels with handle types none, contact and room */
-    { TP_IFACE_CHANNEL_TYPE_TEXT, TP_HANDLE_TYPE_NONE  },
-    { TP_IFACE_CHANNEL_TYPE_TEXT, TP_HANDLE_TYPE_CONTACT  },
-    { TP_IFACE_CHANNEL_TYPE_TEXT, TP_HANDLE_TYPE_ROOM  },
-    /* file transfer to contacts */
-    { TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, TP_HANDLE_TYPE_CONTACT  },
-    /* stream media to contacts */
-    { TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, TP_HANDLE_TYPE_CONTACT  },
-    /* roomlists */
-    { TP_IFACE_CHANNEL_TYPE_ROOM_LIST, TP_HANDLE_TYPE_NONE },
-  };
-  gchar *capabilities[] = {
-    "org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/ice-udp",
-    "org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/gtalk-p2p",
-    NULL };
-  GHashTable *asv;
-  guint i;
-
-  /* Setup the basic Client.Handler that matches our client filter */
-  filters = g_ptr_array_new ();
-  asv = tp_asv_new (
-        TP_IFACE_CHANNEL ".ChannelType", G_TYPE_STRING,
-           TP_IFACE_CHANNEL_TYPE_TEXT,
-        TP_IFACE_CHANNEL ".TargetHandleType", G_TYPE_INT,
-            TP_HANDLE_TYPE_CONTACT,
-        NULL);
-  g_ptr_array_add (filters, asv);
+  TpConnection *conn;
+  GList *chatrooms, *p;
 
-  d = empathy_dispatcher_new (PACKAGE_NAME, filters, NULL);
+  /* Wait if we are not connected or the TpConnection is not prepared yet */
+  conn = tp_account_get_connection (account);
+  if (conn == NULL)
+    return;
 
-  g_ptr_array_foreach (filters, (GFunc) g_hash_table_destroy, NULL);
-  g_ptr_array_free (filters, TRUE);
+  chatrooms = empathy_chatroom_manager_get_chatrooms (
+          chatroom_manager, account);
 
-  /* Setup the an extended Client.Handler that matches everything we can do */
-  filters = g_ptr_array_new ();
-  for (i = 0 ; i < G_N_ELEMENTS (types); i++)
+  for (p = chatrooms; p != NULL; p = p->next)
     {
-      asv = tp_asv_new (
-        TP_IFACE_CHANNEL ".ChannelType", G_TYPE_STRING, types[i].channeltype,
-        TP_IFACE_CHANNEL ".TargetHandleType", G_TYPE_INT, types[i].handletype,
-        NULL);
+      EmpathyChatroom *room = EMPATHY_CHATROOM (p->data);
 
-      g_ptr_array_add (filters, asv);
-    }
+      if (!empathy_chatroom_get_auto_connect (room))
+        continue;
 
-  asv = tp_asv_new (
-        TP_IFACE_CHANNEL ".ChannelType",
-          G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
-        TP_IFACE_CHANNEL ".TargetHandleType",
-          G_TYPE_INT, TP_HANDLE_TYPE_CONTACT,
-        TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio",
-          G_TYPE_BOOLEAN, TRUE,
-        NULL);
-  g_ptr_array_add (filters, asv);
-
-  asv = tp_asv_new (
-        TP_IFACE_CHANNEL ".ChannelType",
-          G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
-        TP_IFACE_CHANNEL ".TargetHandleType",
-          G_TYPE_INT, TP_HANDLE_TYPE_CONTACT,
-        TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo",
-          G_TYPE_BOOLEAN, TRUE,
-        NULL);
-  g_ptr_array_add (filters, asv);
-
-
-  empathy_dispatcher_add_handler (d, PACKAGE_NAME"MoreThanMeetsTheEye",
-    filters, capabilities);
-
-  g_ptr_array_foreach (filters, (GFunc) g_hash_table_destroy, NULL);
-  g_ptr_array_free (filters, TRUE);
-
-  return d;
+      empathy_join_muc (account, empathy_chatroom_get_room (room),
+        TP_USER_ACTION_TIME_NOT_USER_ACTION);
+    }
+  g_list_free (chatrooms);
 }
 
 static void
-account_status_changed_cb (TpAccount *account,
-    guint old_status,
-    guint new_status,
-    guint reason,
-    gchar *dbus_error_name,
-    GHashTable *details,
-    EmpathyChatroom *room)
+account_connection_changed_cb (TpAccount *account,
+    GParamSpec *spec,
+    EmpathyChatroomManager *manager)
 {
-  TpConnection *conn;
-
-  conn = tp_account_get_connection (account);
-  if (conn == NULL)
-    return;
-
-  empathy_dispatcher_join_muc (conn,
-      empathy_chatroom_get_room (room), NULL, NULL);
+  account_join_chatrooms (account, manager);
 }
 
 static void
@@ -505,7 +717,7 @@ account_manager_chatroom_ready_cb (GObject *source_object,
   GList *accounts, *l;
   GError *error = NULL;
 
-  if (!tp_account_manager_prepare_finish (account_manager, result, &error))
+  if (!tp_proxy_prepare_finish (account_manager, result, &error))
     {
       DEBUG ("Failed to prepare account manager: %s", error->message);
       g_error_free (error);
@@ -517,36 +729,14 @@ account_manager_chatroom_ready_cb (GObject *source_object,
   for (l = accounts; l != NULL; l = g_list_next (l))
     {
       TpAccount *account = TP_ACCOUNT (l->data);
-      TpConnection *conn;
-      GList *chatrooms, *p;
 
-      conn = tp_account_get_connection (account);
+      /* Try to join all rooms if we're connected */
+      account_join_chatrooms (account, chatroom_manager);
 
-      chatrooms = empathy_chatroom_manager_get_chatrooms (
-          chatroom_manager, account);
-
-      for (p = chatrooms; p != NULL; p = p->next)
-        {
-          EmpathyChatroom *room = EMPATHY_CHATROOM (p->data);
-
-          if (!empathy_chatroom_get_auto_connect (room))
-            continue;
-
-          if (conn == NULL)
-            {
-              g_signal_connect (G_OBJECT (account), "status-changed",
-                  G_CALLBACK (account_status_changed_cb), room);
-            }
-          else
-            {
-              empathy_dispatcher_join_muc (conn,
-                  empathy_chatroom_get_room (room), NULL, NULL);
-            }
-        }
-
-      g_list_free (chatrooms);
+      /* And/or join them on (re)connection */
+      tp_g_signal_connect_object (account, "notify::connection",
+        G_CALLBACK (account_connection_changed_cb), chatroom_manager, 0);
     }
-
   g_list_free (accounts);
 }
 
@@ -557,190 +747,131 @@ chatroom_manager_ready_cb (EmpathyChatroomManager *chatroom_manager,
 {
   TpAccountManager *account_manager = user_data;
 
-  tp_account_manager_prepare_async (account_manager, NULL,
+  tp_proxy_prepare_async (account_manager, NULL,
       account_manager_chatroom_ready_cb, chatroom_manager);
 }
 
-int
-main (int argc, char *argv[])
+static void
+empathy_app_constructed (GObject *object)
 {
-#if HAVE_GEOCLUE
-  EmpathyLocationManager *location_manager = NULL;
-#endif
-  EmpathyStatusIcon *icon;
-  EmpathyDispatcher *dispatcher;
-  TpAccountManager *account_manager;
-#ifdef ENABLE_TPL
-  TplLogManager *log_manager;
-#else
-  EmpathyLogManager *log_manager;
-#endif /* ENABLE_TPL */
-  EmpathyChatroomManager *chatroom_manager;
-  EmpathyCallFactory *call_factory;
-  EmpathyFTFactory  *ft_factory;
-  GtkWidget *window;
-  EmpathyIdle *idle;
-  EmpathyConnectivity *connectivity;
-  GError *error = NULL;
-  UniqueApp *unique_app;
+  EmpathyApp *self = (EmpathyApp *) object;
   gboolean chatroom_manager_ready;
 
-#ifdef ENABLE_DEBUG
-  TpDebugSender *debug_sender;
-#endif /* ENABLE_TPL */
-
-  GOptionContext *optcontext;
-  GOptionEntry options[] = {
-      { "no-connect", 'n',
-        0, G_OPTION_ARG_NONE, &no_connect,
-        N_("Don't connect on startup"),
-        NULL },
-      { "start-hidden", 'h',
-        0, G_OPTION_ARG_NONE, &start_hidden,
-        N_("Don't display the contact list or any other dialogs on startup"),
-        NULL },
-      { "version", 'v',
-        G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_version_cb,
-        NULL, NULL },
-      { NULL }
-  };
-
-  /* Init */
-  g_thread_init (NULL);
-  empathy_init ();
-
-  optcontext = g_option_context_new (N_("- Empathy IM Client"));
-  g_option_context_add_group (optcontext, gst_init_get_option_group ());
-  g_option_context_add_group (optcontext, gtk_get_option_group (TRUE));
-#if HAVE_LIBCHAMPLAIN
-  g_option_context_add_group (optcontext, clutter_get_option_group ());
-#endif
-  g_option_context_add_main_entries (optcontext, options, GETTEXT_PACKAGE);
-
-  if (!g_option_context_parse (optcontext, &argc, &argv, &error)) {
-    g_print ("%s\nRun '%s --help' to see a full list of available command line options.\n",
-        error->message, argv[0]);
-    g_warning ("Error in empathy init: %s", error->message);
-    return EXIT_FAILURE;
-  }
-
-  g_option_context_free (optcontext);
-
-  empathy_gtk_init ();
   g_set_application_name (_(PACKAGE_NAME));
-  g_setenv ("PULSE_PROP_media.role", "phone", TRUE);
 
   gtk_window_set_default_icon_name ("empathy");
   textdomain (GETTEXT_PACKAGE);
 
 #ifdef ENABLE_DEBUG
   /* Set up debug sender */
-  debug_sender = tp_debug_sender_dup ();
+  self->debug_sender = tp_debug_sender_dup ();
   g_log_set_default_handler (tp_debug_sender_log_handler, G_LOG_DOMAIN);
 #endif
 
-  unique_app = unique_app_new ("org.gnome."PACKAGE_NAME, NULL);
-
-  if (unique_app_is_running (unique_app))
-    {
-      unique_app_send_message (unique_app, UNIQUE_ACTIVATE, NULL);
-
-      g_object_unref (unique_app);
-      return EXIT_SUCCESS;
-    }
-
   notify_init (_(PACKAGE_NAME));
 
   /* Setting up Idle */
-  idle = empathy_idle_dup_singleton ();
-  empathy_idle_set_auto_away (idle, TRUE);
+  self->presence_mgr = empathy_presence_manager_dup_singleton ();
+
+  self->gsettings = g_settings_new (EMPATHY_PREFS_SCHEMA);
 
   /* Setting up Connectivity */
-  connectivity = empathy_connectivity_dup_singleton ();
-  use_conn_notify_cb (empathy_conf_get (), EMPATHY_PREFS_USE_CONN,
-      connectivity);
-  empathy_conf_notify_add (empathy_conf_get (), EMPATHY_PREFS_USE_CONN,
-      use_conn_notify_cb, connectivity);
+  self->connectivity = empathy_connectivity_dup_singleton ();
+  use_conn_notify_cb (self->gsettings, EMPATHY_PREFS_USE_CONN,
+      self->connectivity);
+  g_signal_connect (self->gsettings,
+      "changed::" EMPATHY_PREFS_USE_CONN,
+      G_CALLBACK (use_conn_notify_cb), self->connectivity);
 
   /* account management */
-  account_manager = tp_account_manager_dup ();
-  tp_account_manager_prepare_async (account_manager, NULL,
-      account_manager_ready_cb, NULL);
-
-  /* Handle channels */
-  dispatcher = setup_dispatcher ();
-  g_signal_connect (dispatcher, "dispatch", G_CALLBACK (dispatch_cb), NULL);
+  self->account_manager = tp_account_manager_dup ();
+  tp_proxy_prepare_async (self->account_manager, NULL,
+      account_manager_ready_cb, self);
 
   migrate_config_to_xdg_dir ();
 
-  /* Setting up UI */
-  window = empathy_main_window_show ();
-  icon = empathy_status_icon_new (GTK_WINDOW (window), start_hidden);
-
-  g_signal_connect (unique_app, "message-received",
-      G_CALLBACK (unique_app_message_cb), window);
-
   /* Logging */
-#ifdef ENABLE_TPL
-  log_manager = tpl_log_manager_dup_singleton ();
-#else
-  log_manager = empathy_log_manager_dup_singleton ();
-  empathy_log_manager_observe (log_manager, dispatcher);
-#endif
+  self->log_manager = tpl_log_manager_dup_singleton ();
 
-  chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
-  empathy_chatroom_manager_observe (chatroom_manager, dispatcher);
+  self->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
 
-  g_object_get (chatroom_manager, "ready", &chatroom_manager_ready, NULL);
+  g_object_get (self->chatroom_manager, "ready", &chatroom_manager_ready, NULL);
   if (!chatroom_manager_ready)
     {
-      g_signal_connect (G_OBJECT (chatroom_manager), "notify::ready",
-          G_CALLBACK (chatroom_manager_ready_cb), account_manager);
+      g_signal_connect (G_OBJECT (self->chatroom_manager), "notify::ready",
+          G_CALLBACK (chatroom_manager_ready_cb), self->account_manager);
     }
   else
     {
-      chatroom_manager_ready_cb (chatroom_manager, NULL, account_manager);
+      chatroom_manager_ready_cb (self->chatroom_manager, NULL,
+          self->account_manager);
     }
 
-  /* Create the call factory */
-  call_factory = empathy_call_factory_initialise ();
-  g_signal_connect (G_OBJECT (call_factory), "new-call-handler",
-      G_CALLBACK (new_call_handler_cb), NULL);
-  /* Create the FT factory */
-  ft_factory = empathy_ft_factory_dup_singleton ();
-  g_signal_connect (ft_factory, "new-ft-handler",
-      G_CALLBACK (new_ft_handler_cb), NULL);
-  g_signal_connect (ft_factory, "new-incoming-transfer",
-      G_CALLBACK (new_incoming_transfer_cb), NULL);
-
   /* Location mananger */
-#if HAVE_GEOCLUE
-  location_manager = empathy_location_manager_dup_singleton ();
+#ifdef HAVE_GEOCLUE
+  self->location_manager = empathy_location_manager_dup_singleton ();
 #endif
 
-  gtk_main ();
+  self->conn_aggregator = empathy_connection_aggregator_dup_singleton ();
+
+  self->activated = FALSE;
+  self->ft_factory = NULL;
+  self->window = NULL;
+}
 
-  empathy_idle_set_state (idle, TP_CONNECTION_PRESENCE_TYPE_OFFLINE);
+static void
+add_empathy_features (void)
+{
+  /* Add 'empathy' specific feature before doing any preparation */
+  EmpathyClientFactory *factory;
+
+  factory = empathy_client_factory_dup ();
+
+  tp_simple_client_factory_add_connection_features_varargs (
+      TP_SIMPLE_CLIENT_FACTORY (factory),
+      /* empathy_connection_aggregator_get_all_groups(), used by
+       * EmpathyGroupsWidget relies on it */
+      TP_CONNECTION_FEATURE_CONTACT_GROUPS,
+      /* empathy_connection_aggregator_dup_all_contacts(), used by
+       * EmpathyEventManager relies on it */
+      TP_CONNECTION_FEATURE_CONTACT_LIST,
+      NULL);
 
-#ifdef ENABLE_DEBUG
-  g_object_unref (debug_sender);
-#endif
+  g_object_unref (factory);
+}
+
+int
+main (int argc, char *argv[])
+{
+  EmpathyApp *app;
+  gint retval;
 
-  g_object_unref (idle);
-  g_object_unref (connectivity);
-  g_object_unref (icon);
-  g_object_unref (account_manager);
-  g_object_unref (log_manager);
-  g_object_unref (dispatcher);
-  g_object_unref (chatroom_manager);
-#if HAVE_GEOCLUE
-  g_object_unref (location_manager);
+  g_thread_init (NULL);
+  g_type_init ();
+
+#ifdef HAVE_LIBCHAMPLAIN
+  gtk_clutter_init (&argc, &argv);
 #endif
-  g_object_unref (ft_factory);
-  g_object_unref (unique_app);
+
+  g_type_init ();
+  tpy_cli_init ();
+  empathy_init ();
+  gtk_init (&argc, &argv);
+  empathy_gtk_init ();
+
+  add_empathy_features ();
+
+  app = g_object_new (EMPATHY_TYPE_APP,
+      "application-id", EMPATHY_DBUS_NAME,
+      "flags", G_APPLICATION_HANDLES_COMMAND_LINE,
+      NULL);
+
+  retval = g_application_run (G_APPLICATION (app), argc, argv);
 
   notify_uninit ();
   xmlCleanupParser ();
 
-  return EXIT_SUCCESS;
+  g_object_unref (app);
+
+  return retval;
 }