]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-account-widget.c
use gtk_box_new() instead of gtk_[h,v]box_new()
[empathy.git] / libempathy-gtk / empathy-account-widget.c
index d3cf1f3e41ca8b73b7692bcc2ae16c8acdef4505..f1b463c039ebf226dcf89b0567be5fa8c3daecbe 100644 (file)
@@ -30,9 +30,7 @@
 #include <gtk/gtk.h>
 #include <glib/gi18n-lib.h>
 
-#ifdef HAVE_MEEGO
-#include <mx-gtk/mx-gtk.h>
-#endif /* HAVE_MEEGO */
+#include <gio/gdesktopappinfo.h>
 
 #include <libempathy/empathy-utils.h>
 
 
 G_DEFINE_TYPE (EmpathyAccountWidget, empathy_account_widget, G_TYPE_OBJECT)
 
+typedef enum
+{
+  NO_SERVICE = 0,
+  GTALK_SERVICE,
+  FACEBOOK_SERVICE,
+  N_SERVICES
+} Service;
+
+typedef struct
+{
+  const gchar *label_username_example;
+  gboolean show_advanced;
+} ServiceInfo;
+
+static ServiceInfo services_infos[N_SERVICES] = {
+    { "label_username_example", TRUE },
+    { "label_username_g_example", FALSE },
+    { "label_username_f_example", FALSE },
+};
+
 typedef struct {
   EmpathyAccountSettings *settings;
 
-  GtkWidget *table_common_settings;
+  GtkWidget *grid_common_settings;
   GtkWidget *apply_button;
   GtkWidget *cancel_button;
   GtkWidget *entry_password;
   GtkWidget *spinbutton_port;
-  GtkWidget *enabled_checkbox;
   GtkWidget *radiobutton_reuse;
 
   gboolean simple;
@@ -88,9 +105,16 @@ typedef struct {
   GtkWidget *param_account_widget;
   GtkWidget *param_password_widget;
 
+  gboolean automatic_change;
+  GtkWidget *remember_password_widget;
+
   /* Used only for IRC accounts */
   EmpathyIrcNetworkChooser *irc_network_chooser;
 
+  /* Used for 'special' XMPP account having a service associated ensuring that
+   * JIDs have a specific suffix; such as Facebook for example */
+  gchar *jid_suffix;
+
   gboolean dispose_run;
 } EmpathyAccountWidgetPriv;
 
@@ -109,11 +133,76 @@ enum {
   LAST_SIGNAL
 };
 
+static void account_widget_apply_and_log_in (EmpathyAccountWidget *);
+
+enum {
+  RESPONSE_LAUNCH
+};
+
 static guint signals[LAST_SIGNAL] = { 0 };
 
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountWidget)
 #define CHANGED_TIMEOUT 300
 
+#define DIGIT             "0-9"
+#define DIGITS            "(["DIGIT"]+)"
+#define ALPHA             "a-zA-Z"
+#define ALPHAS            "(["ALPHA"]+)"
+#define ALPHADIGIT        ALPHA DIGIT
+#define ALPHADIGITS       "(["ALPHADIGIT"]+)"
+#define ALPHADIGITDASH    ALPHA DIGIT "-"
+#define ALPHADIGITDASHS   "(["ALPHADIGITDASH"]*)"
+
+#define HOSTNUMBER        "("DIGITS"\\."DIGITS"\\."DIGITS"\\."DIGITS")"
+#define TOPLABEL          "("ALPHAS \
+                            "| (["ALPHA"]"ALPHADIGITDASHS "["ALPHADIGIT"]))"
+#define DOMAINLABEL       "("ALPHADIGITS"|(["ALPHADIGIT"]" ALPHADIGITDASHS \
+                                       "["ALPHADIGIT"]))"
+#define HOSTNAME          "((" DOMAINLABEL "\\.)+" TOPLABEL ")"
+/* Based on http://www.ietf.org/rfc/rfc1738.txt (section 5) */
+#define HOST              "("HOSTNAME "|" HOSTNUMBER")"
+/* Based on http://www.ietf.org/rfc/rfc0822.txt (appendix D) */
+#define EMAIL_LOCALPART   "([^\\(\\)<>@,;:\\\\\"\\[\\]\\s]+)"
+
+/* UIN is digital according to the unofficial specification:
+ * http://iserverd.khstu.ru/docum_ext/icqv5.html#CTS
+ * 5 digits minimum according to http://en.wikipedia.org/wiki/ICQ#UIN
+ * According to an user, we can also provide an email address instead of the
+ * ICQ UIN. */
+#define ICQ_USER_NAME     "((["DIGIT"]{5,})|"EMAIL_LOCALPART"@"HOST")"
+
+/* Based on http://www.ietf.org/rfc/rfc2812.txt (section 2.3.1) */
+#define IRC_SPECIAL       "_\\[\\]{}\\\\|`^"
+#define IRC_NICK_NAME     "(["ALPHA IRC_SPECIAL"]["ALPHADIGITDASH IRC_SPECIAL"]*)"
+/*   user       =  1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF )
+ *                ; any octet except NUL, CR, LF, " " and "@"
+ *
+ * so technically, like so many other places in IRC, we should be using arrays
+ * of bytes here rather than UTF-8 strings. Life: too short. In practice this
+ * will always be ASCII.
+ */
+#define IRC_USER_NAME     "([^\r\n@ ])+"
+
+/* Based on http://www.ietf.org/rfc/rfc4622.txt (section 2.2)
+ * We just exclude invalid characters to avoid ucschars and other redundant
+ * complexity */
+#define JABBER_USER_NAME  "([^@:'\"<>&\\s]+)"
+/* ID is an email according to the unofficial specification:
+ * http://www.hypothetic.org/docs/msn/general/names.php */
+#define MSN_USER_NAME     EMAIL_LOCALPART
+/* Based on the official help:
+ * http://help.yahoo.com/l/us/yahoo/edit/registration/edit-01.html
+ * Looks like an email address can be used as well (bgo #655959)
+ * */
+#define YAHOO_USER_NAME   "(["ALPHA"]["ALPHADIGIT"_\\.]{3,31})|("EMAIL_LOCALPART"@"HOST")"
+
+#define ACCOUNT_REGEX_ICQ      "^"ICQ_USER_NAME"$"
+#define ACCOUNT_REGEX_IRC      "^"IRC_NICK_NAME"$"
+#define USERNAME_REGEX_IRC     "^"IRC_USER_NAME"$"
+#define ACCOUNT_REGEX_JABBER   "^"JABBER_USER_NAME"@"HOST"$"
+#define ACCOUNT_REGEX_MSN      "^"MSN_USER_NAME"@"HOST"$"
+#define ACCOUNT_REGEX_YAHOO    "^"YAHOO_USER_NAME"$"
+
 static void
 account_widget_set_control_buttons_sensitivity (EmpathyAccountWidget *self,
     gboolean sensitive)
@@ -130,6 +219,51 @@ account_widget_set_control_buttons_sensitivity (EmpathyAccountWidget *self,
       gtk_widget_set_sensitive (priv->apply_button, sensitive);
       gtk_widget_set_sensitive (priv->cancel_button,
           (sensitive || priv->creating_account) && priv->other_accounts_exist);
+
+      if (sensitive)
+        {
+          /* We can't grab default if the widget hasn't be packed in a
+           * window */
+          GtkWidget *window;
+
+          window = gtk_widget_get_toplevel (priv->apply_button);
+          if (window != NULL &&
+              gtk_widget_is_toplevel (window))
+            {
+              gtk_widget_set_can_default (priv->apply_button, TRUE);
+              gtk_widget_grab_default (priv->apply_button);
+            }
+        }
+    }
+}
+
+static void
+account_widget_set_entry_highlighting (GtkEntry *entry,
+    gboolean highlight)
+{
+  g_return_if_fail (GTK_IS_ENTRY (entry));
+
+  if (highlight)
+    {
+      GtkStyleContext *style;
+      GdkRGBA color;
+
+      style = gtk_widget_get_style_context (GTK_WIDGET (entry));
+      gtk_style_context_get_background_color (style, GTK_STATE_FLAG_SELECTED,
+          &color);
+
+      /* Here we take the current theme colour and add it to
+       * the colour for white and average the two. This
+       * gives a colour which is inline with the theme but
+       * slightly whiter.
+       */
+      empathy_make_color_whiter (&color);
+
+      gtk_widget_override_background_color (GTK_WIDGET (entry), 0, &color);
+    }
+  else
+    {
+      gtk_widget_override_background_color (GTK_WIDGET (entry), 0, NULL);
     }
 }
 
@@ -154,9 +288,13 @@ account_widget_entry_changed_common (EmpathyAccountWidget *self,
   const gchar *str;
   const gchar *param_name;
   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+  gboolean prev_status;
+  gboolean curr_status;
 
   str = gtk_entry_get_text (entry);
   param_name = g_object_get_data (G_OBJECT (entry), "param_name");
+  prev_status = empathy_account_settings_parameter_is_valid (priv->settings,
+                                                             param_name);
 
   if (EMP_STR_EMPTY (str))
     {
@@ -178,16 +316,41 @@ account_widget_entry_changed_common (EmpathyAccountWidget *self,
           tp_strdiff (param_name, "password") ? str : "***");
       empathy_account_settings_set_string (priv->settings, param_name, str);
     }
+
+  curr_status = empathy_account_settings_parameter_is_valid (priv->settings,
+                                                             param_name);
+  if (curr_status != prev_status)
+    account_widget_set_entry_highlighting (entry, !curr_status);
 }
 
 static void
 account_widget_entry_changed_cb (GtkEditable *entry,
     EmpathyAccountWidget *self)
 {
+  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+
+  if (priv->automatic_change)
+    return;
+
   account_widget_entry_changed_common (self, GTK_ENTRY (entry), FALSE);
   empathy_account_widget_changed (self);
 }
 
+static void
+account_widget_entry_map_cb (GtkEntry *entry,
+    EmpathyAccountWidget *self)
+{
+  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+  const gchar *param_name;
+  gboolean is_valid;
+
+  /* need to initialize input highlighting */
+  param_name = g_object_get_data (G_OBJECT (entry), "param_name");
+  is_valid = empathy_account_settings_parameter_is_valid (priv->settings,
+                                                          param_name);
+  account_widget_set_entry_highlighting (entry, !is_valid);
+}
+
 static void
 account_widget_int_changed_cb (GtkWidget *widget,
     EmpathyAccountWidget *self)
@@ -357,6 +520,26 @@ password_entry_changed_cb (GtkEditable *entry,
       GTK_ENTRY_ICON_SECONDARY, !EMP_STR_EMPTY (str));
 }
 
+static void
+password_entry_activated_cb (GtkEntry *entry,
+    EmpathyAccountWidget *self)
+{
+    EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+
+    if (gtk_widget_get_sensitive (priv->apply_button))
+        account_widget_apply_and_log_in (self);
+}
+
+static void
+account_entry_activated_cb (GtkEntry *entry,
+    EmpathyAccountWidget *self)
+{
+    EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+
+    if (gtk_widget_get_sensitive (priv->apply_button))
+        account_widget_apply_and_log_in (self);
+}
+
 void
 empathy_account_widget_setup_widget (EmpathyAccountWidget *self,
     GtkWidget *widget,
@@ -433,10 +616,18 @@ empathy_account_widget_setup_widget (EmpathyAccountWidget *self,
               G_CALLBACK (clear_icon_released_cb), self);
           g_signal_connect (widget, "changed",
               G_CALLBACK (password_entry_changed_cb), self);
+          g_signal_connect (widget, "activate",
+              G_CALLBACK (password_entry_activated_cb), self);
         }
+      else if (strstr (param_name, "account"))
+        g_signal_connect (widget, "activate",
+            G_CALLBACK (account_entry_activated_cb), self);
+
 
       g_signal_connect (widget, "changed",
           G_CALLBACK (account_widget_entry_changed_cb), self);
+      g_signal_connect (widget, "map",
+          G_CALLBACK (account_widget_entry_map_cb), self);
     }
   else if (GTK_IS_TOGGLE_BUTTON (widget))
     {
@@ -489,6 +680,23 @@ empathy_account_widget_setup_widget (EmpathyAccountWidget *self,
     {
       DEBUG ("Unknown type of widget for param %s", param_name);
     }
+
+  gtk_widget_set_sensitive (widget,
+      empathy_account_settings_param_is_supported (priv->settings, param_name));
+}
+
+static GHashTable *
+build_translated_params (void)
+{
+  GHashTable *hash;
+
+  hash = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (hash, "account", _("Account"));
+  g_hash_table_insert (hash, "password", _("Password"));
+  g_hash_table_insert (hash, "server", _("Server"));
+  g_hash_table_insert (hash, "port", _("Port"));
+
+  return hash;
 }
 
 static gchar *
@@ -496,6 +704,15 @@ account_widget_generic_format_param_name (const gchar *param_name)
 {
   gchar *str;
   gchar *p;
+  static GHashTable *translated_params = NULL;
+
+  if (G_UNLIKELY (translated_params == NULL))
+    translated_params = build_translated_params ();
+
+  /* Translate most common parameters */
+  str = g_hash_table_lookup (translated_params, param_name);
+  if (str != NULL)
+    return g_strdup (str);
 
   str = g_strdup (param_name);
 
@@ -518,32 +735,39 @@ account_widget_generic_format_param_name (const gchar *param_name)
 
 static void
 accounts_widget_generic_setup (EmpathyAccountWidget *self,
-    GtkWidget *table_common_settings,
-    GtkWidget *table_advanced_settings)
+    GtkWidget *grid_common_settings,
+    GtkWidget *grid_advanced_settings)
 {
   TpConnectionManagerParam *params, *param;
   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+  guint row_common = 0, row_advanced = 0;
 
   params = empathy_account_settings_get_tp_params (priv->settings);
 
   for (param = params; param != NULL && param->name != NULL; param++)
     {
-      GtkWidget       *table_settings;
-      guint            n_rows = 0;
+      GtkWidget       *grid_settings;
+      guint           row;
       GtkWidget       *widget = NULL;
       gchar           *param_name_formatted;
 
       if (param->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED)
-        table_settings = table_common_settings;
+        {
+          grid_settings = grid_common_settings;
+          row = row_common++;
+        }
       else if (priv->simple)
-        return;
+        {
+          return;
+        }
       else
-        table_settings = table_advanced_settings;
+        {
+          grid_settings = grid_advanced_settings;
+          row = row_advanced++;
+        }
 
       param_name_formatted = account_widget_generic_format_param_name
         (param->name);
-      g_object_get (table_settings, "n-rows", &n_rows, NULL);
-      gtk_table_resize (GTK_TABLE (table_settings), ++n_rows, 2);
 
       if (param->dbus_signature[0] == 's')
         {
@@ -554,12 +778,9 @@ accounts_widget_generic_setup (EmpathyAccountWidget *self,
           gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
           g_free (str);
 
-          gtk_table_attach (GTK_TABLE (table_settings),
-              widget,
-              0, 1,
-              n_rows - 1, n_rows,
-              GTK_FILL, 0,
-              0, 0);
+          gtk_grid_attach (GTK_GRID (grid_settings),
+              widget, 0, row, 1, 1);
+
           gtk_widget_show (widget);
 
           widget = gtk_entry_new ();
@@ -569,12 +790,10 @@ accounts_widget_generic_setup (EmpathyAccountWidget *self,
                   G_CALLBACK (gtk_widget_grab_focus),
                   NULL);
             }
-          gtk_table_attach (GTK_TABLE (table_settings),
-              widget,
-              1, 2,
-              n_rows - 1, n_rows,
-              GTK_FILL | GTK_EXPAND, 0,
-              0, 0);
+
+          gtk_grid_attach (GTK_GRID (grid_settings),
+              widget, 1, row, 1, 1);
+
           gtk_widget_show (widget);
         }
       /* int types: ynqiuxt. double type is 'd' */
@@ -611,32 +830,20 @@ accounts_widget_generic_setup (EmpathyAccountWidget *self,
           gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
           g_free (str);
 
-          gtk_table_attach (GTK_TABLE (table_settings),
-              widget,
-              0, 1,
-              n_rows - 1, n_rows,
-              GTK_FILL, 0,
-              0, 0);
+          gtk_grid_attach (GTK_GRID (grid_settings),
+              widget, 0, row, 1, 1);
           gtk_widget_show (widget);
 
           widget = gtk_spin_button_new_with_range (minint, maxint, step);
-          gtk_table_attach (GTK_TABLE (table_settings),
-              widget,
-              1, 2,
-              n_rows - 1, n_rows,
-              GTK_FILL | GTK_EXPAND, 0,
-              0, 0);
+          gtk_grid_attach (GTK_GRID (grid_settings),
+              widget, 1, row, 1, 1);
           gtk_widget_show (widget);
         }
       else if (param->dbus_signature[0] == 'b')
         {
           widget = gtk_check_button_new_with_label (param_name_formatted);
-          gtk_table_attach (GTK_TABLE (table_settings),
-              widget,
-              0, 2,
-              n_rows - 1, n_rows,
-              GTK_FILL | GTK_EXPAND, 0,
-              0, 0);
+          gtk_grid_attach (GTK_GRID (grid_settings),
+              widget, 0, row, 2, 1);
           gtk_widget_show (widget);
         }
       else
@@ -721,8 +928,10 @@ account_widget_applied_cb (GObject *source_object,
   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (source_object);
   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
+  gboolean reconnect_required;
 
-  empathy_account_settings_apply_finish (settings, res, &error);
+  empathy_account_settings_apply_finish (settings, res, &reconnect_required,
+      &error);
 
   if (error != NULL)
     {
@@ -746,23 +955,21 @@ account_widget_applied_cb (GObject *source_object,
               account_widget_account_enabled_cb, widget);
           g_signal_emit (widget, signals[ACCOUNT_CREATED], 0, account);
         }
-      else if (priv->enabled_checkbox != NULL)
+      else
         {
-          gboolean enabled_checked;
-
-          enabled_checked =
-#ifdef HAVE_MEEGO
-            mx_gtk_light_switch_get_active (
-                MX_GTK_LIGHT_SWITCH (priv->enabled_checkbox));
-#else
-            gtk_toggle_button_get_active (
-                GTK_TOGGLE_BUTTON (priv->enabled_checkbox));
-#endif /* HAVE_MEEGO */
-
-          if (tp_account_is_enabled (account) && enabled_checked)
+          /* If the account was offline, we always want to try reconnecting,
+           * to give it a chance to connect if the previous params were wrong.
+           * tp_account_reconnect_async() won't do anything if the requested
+           * presence is offline anyway. */
+          if (tp_account_get_connection_status (account, NULL) ==
+              TP_CONNECTION_STATUS_DISCONNECTED)
+            reconnect_required = TRUE;
+
+          if (reconnect_required && tp_account_is_enabled (account)
+              && tp_account_is_enabled (account))
             {
               /* After having applied changes to a user account, we
-               * automatically reconnect it. This is done so the new
+               * reconnect it if needed. This is done so the new
                * information entered by the user is validated on the server. */
               tp_account_reconnect_async (account, NULL, NULL);
             }
@@ -779,8 +986,7 @@ account_widget_applied_cb (GObject *source_object,
 }
 
 static void
-account_widget_apply_clicked_cb (GtkWidget *button,
-    EmpathyAccountWidget *self)
+account_widget_apply_and_log_in (EmpathyAccountWidget *self)
 {
   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
   gboolean display_name_overridden;
@@ -817,19 +1023,26 @@ account_widget_apply_clicked_cb (GtkWidget *button,
       account_widget_applied_cb, self);
 }
 
+static void
+account_widget_apply_clicked_cb (GtkWidget *button,
+    EmpathyAccountWidget *self)
+{
+    account_widget_apply_and_log_in (self);
+}
+
 static void
 account_widget_setup_generic (EmpathyAccountWidget *self)
 {
-  GtkWidget *table_common_settings;
-  GtkWidget *table_advanced_settings;
+  GtkWidget *grid_common_settings;
+  GtkWidget *grid_advanced_settings;
 
-  table_common_settings = GTK_WIDGET (gtk_builder_get_object
-      (self->ui_details->gui, "table_common_settings"));
-  table_advanced_settings = GTK_WIDGET (gtk_builder_get_object
-      (self->ui_details->gui, "table_advanced_settings"));
+  grid_common_settings = GTK_WIDGET (gtk_builder_get_object
+      (self->ui_details->gui, "grid_common_settings"));
+  grid_advanced_settings = GTK_WIDGET (gtk_builder_get_object
+      (self->ui_details->gui, "grid_advanced_settings"));
 
-  accounts_widget_generic_setup (self, table_common_settings,
-      table_advanced_settings);
+  accounts_widget_generic_setup (self, grid_common_settings,
+      grid_advanced_settings);
 
   g_object_unref (self->ui_details->gui);
 }
@@ -854,7 +1067,7 @@ account_widget_build_generic (EmpathyAccountWidget *self,
   GtkWidget *expander_advanced;
 
   self->ui_details->gui = empathy_builder_get_file (filename,
-      "table_common_settings", &priv->table_common_settings,
+      "grid_common_settings", &priv->grid_common_settings,
       "vbox_generic_settings", &self->ui_details->widget,
       "expander_advanced_settings", &expander_advanced,
       NULL);
@@ -871,6 +1084,175 @@ account_widget_build_generic (EmpathyAccountWidget *self,
         G_CALLBACK (account_widget_settings_ready_cb), self);
 }
 
+static void
+account_widget_launch_external_clicked (GtkWidget *button,
+    TpAccount *account)
+{
+  GdkAppLaunchContext *context = NULL;
+  GdkDisplay *display;
+  GAppInfo *app_info;
+  GError *error = NULL;
+
+  app_info = g_object_get_data (G_OBJECT (button), "app-info");
+
+  g_return_if_fail (G_IS_APP_INFO (app_info));
+
+  display = gdk_display_get_default ();
+  context = gdk_display_get_app_launch_context (display);
+
+  if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
+        &error))
+    {
+      g_critical ("Failed to bisho: %s", error->message);
+      g_clear_error (&error);
+    }
+}
+
+static void
+account_widget_launch_external_clicked_meego (GtkWidget *button,
+    TpAccount *account)
+{
+  if (!tp_strdiff (tp_account_get_storage_provider (account),
+        "com.meego.libsocialweb"))
+    {
+      /* we know how to handle this external provider */
+      GDesktopAppInfo *desktop_info;
+      GError *error = NULL;
+      GdkAppLaunchContext *context = NULL;
+      GdkDisplay *display;
+      gchar *cmd;
+      GAppInfo *app_info;
+
+      desktop_info = g_desktop_app_info_new ("gnome-control-center.desktop");
+      if (desktop_info == NULL)
+        {
+          g_critical ("Could not locate 'gnome-control-center.desktop'");
+          return;
+        }
+
+      /* glib doesn't have API to start a desktop file with args... (#637875) */
+      cmd = g_strdup_printf ("%s bisho.desktop", g_app_info_get_commandline (
+            (GAppInfo *) desktop_info));
+
+      app_info = g_app_info_create_from_commandline (cmd, NULL, 0, &error);
+      g_free (cmd);
+
+      if (app_info == NULL)
+        {
+          DEBUG ("Failed to create app info: %s", error->message);
+          g_error_free (error);
+          goto out;
+        }
+
+      display = gdk_display_get_default ();
+      context = gdk_display_get_app_launch_context (display);
+
+      if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
+            &error))
+        {
+          g_critical ("Failed to bisho: %s", error->message);
+          g_clear_error (&error);
+        }
+
+out:
+      g_object_unref (desktop_info);
+      tp_clear_object (&app_info);
+      tp_clear_object (&context);
+    }
+}
+
+static void
+account_widget_build_external (EmpathyAccountWidget *self,
+    EmpathyAccountSettings *settings)
+{
+  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+  TpAccount *account = empathy_account_settings_get_account (settings);
+  GtkWidget *bar, *widget;
+  gchar *str;
+  const gchar *provider, *name = NULL;
+  GDesktopAppInfo *desktop_info = NULL;
+
+  self->ui_details->widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+  priv->grid_common_settings = gtk_grid_new ();
+
+  provider = tp_account_get_storage_provider (account);
+
+  if (!tp_strdiff (provider, "com.meego.libsocialweb"))
+    {
+      name = _("My Web Accounts");
+    }
+  else if (!tp_strdiff (provider, "org.gnome.OnlineAccounts"))
+    {
+      /* FIXME: we should publish the .desktop file in some general way */
+      desktop_info = g_desktop_app_info_new (
+          "gnome-online-accounts-panel.desktop");
+
+      if (desktop_info == NULL)
+        g_critical ("Could not locate 'gnome-online-accounts-panel.desktop'");
+      else
+        name = g_app_info_get_name (G_APP_INFO (desktop_info));
+    }
+
+  if (name != NULL)
+    {
+      str = g_strdup_printf (
+          _("The account %s is edited via %s."),
+          empathy_account_settings_get_display_name (settings), name);
+    }
+  else
+    {
+      str = g_strdup_printf (
+          _("The account %s cannot be edited in Empathy."),
+          empathy_account_settings_get_display_name (settings));
+    }
+
+  widget = gtk_label_new (str);
+  gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+  g_free (str);
+
+  bar = gtk_info_bar_new ();
+  gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_INFO);
+  gtk_container_add (
+      GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))),
+      widget);
+  gtk_container_set_border_width (GTK_CONTAINER (bar), 6);
+
+  if (!tp_strdiff (provider, "com.meego.libsocialweb"))
+    {
+      /* we know how to handle this external provider */
+      widget = gtk_info_bar_add_button (GTK_INFO_BAR (bar),
+          _("Launch My Web Accounts"), RESPONSE_LAUNCH);
+
+      g_signal_connect (widget, "clicked",
+          G_CALLBACK (account_widget_launch_external_clicked_meego), account);
+    }
+  else if (desktop_info != NULL)
+    {
+      /* general handler */
+      str = g_strdup_printf (_("Edit %s"), name);
+
+      widget = gtk_info_bar_add_button (GTK_INFO_BAR (bar),
+          str, RESPONSE_LAUNCH);
+
+      g_object_set_data_full (G_OBJECT (widget), "app-info",
+          g_object_ref (desktop_info), g_object_unref);
+
+      g_signal_connect (widget, "clicked",
+          G_CALLBACK (account_widget_launch_external_clicked), account);
+
+      g_free (str);
+    }
+
+  gtk_box_pack_start (GTK_BOX (self->ui_details->widget), bar,
+      FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (self->ui_details->widget),
+      priv->grid_common_settings, FALSE, TRUE, 0);
+
+  gtk_widget_show_all (self->ui_details->widget);
+
+  tp_clear_object (&desktop_info);
+}
+
 static void
 account_widget_build_salut (EmpathyAccountWidget *self,
     const char *filename)
@@ -879,7 +1261,7 @@ account_widget_build_salut (EmpathyAccountWidget *self,
   GtkWidget *expander_advanced;
 
   self->ui_details->gui = empathy_builder_get_file (filename,
-      "table_common_settings", &priv->table_common_settings,
+      "grid_common_settings", &priv->grid_common_settings,
       "vbox_salut_settings", &self->ui_details->widget,
       "expander_advanced_settings", &expander_advanced,
       NULL);
@@ -905,6 +1287,11 @@ account_widget_build_irc (EmpathyAccountWidget *self,
 {
   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
 
+  empathy_account_settings_set_regex (priv->settings, "account",
+      ACCOUNT_REGEX_IRC);
+  empathy_account_settings_set_regex (priv->settings, "username",
+      USERNAME_REGEX_IRC);
+
   if (priv->simple)
     {
       priv->irc_network_chooser = empathy_account_widget_irc_build_simple (self,
@@ -913,7 +1300,7 @@ account_widget_build_irc (EmpathyAccountWidget *self,
   else
     {
       priv->irc_network_chooser = empathy_account_widget_irc_build (self,
-          filename, &priv->table_common_settings);
+          filename, &priv->grid_common_settings);
     }
 }
 
@@ -923,7 +1310,18 @@ account_widget_build_sip (EmpathyAccountWidget *self,
 {
   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
   empathy_account_widget_sip_build (self, filename,
-    &priv->table_common_settings);
+    &priv->grid_common_settings);
+
+  if (priv->simple)
+    {
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password_simple"));
+    }
+  else
+    {
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password"));
+    }
 }
 
 static void
@@ -932,6 +1330,9 @@ account_widget_build_msn (EmpathyAccountWidget *self,
 {
   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
 
+  empathy_account_settings_set_regex (priv->settings, "account",
+      ACCOUNT_REGEX_MSN);
+
   if (priv->simple)
     {
       self->ui_details->gui = empathy_builder_get_file (filename,
@@ -944,11 +1345,14 @@ account_widget_build_msn (EmpathyAccountWidget *self,
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_id_simple");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password_simple"));
     }
   else
     {
       self->ui_details->gui = empathy_builder_get_file (filename,
-          "table_common_msn_settings", &priv->table_common_settings,
+          "grid_common_msn_settings", &priv->grid_common_settings,
           "vbox_msn_settings", &self->ui_details->widget,
           NULL);
 
@@ -960,45 +1364,30 @@ account_widget_build_msn (EmpathyAccountWidget *self,
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_id");
-    }
-}
-
-static gboolean
-account_widget_is_gtalk (EmpathyAccountWidget *self)
-{
-  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
-
-  return !tp_strdiff (empathy_account_settings_get_icon_name (priv->settings),
-      "im-google-talk");
-}
-
-static gboolean
-account_widget_is_facebook (EmpathyAccountWidget *self)
-{
-  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
 
-  return !tp_strdiff (empathy_account_settings_get_icon_name (priv->settings),
-      "im-facebook");
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password"));
+    }
 }
 
-#define FACEBOOK_SUFFIX "@chat.facebook.com"
-
 static void
-facebook_id_widget_changed_cb (GtkWidget *entry,
+suffix_id_widget_changed_cb (GtkWidget *entry,
     EmpathyAccountWidget *self)
 {
   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
   const gchar *account;
 
+  g_assert (priv->jid_suffix != NULL);
+
   account_widget_entry_changed_common (self, GTK_ENTRY (entry), FALSE);
 
   account = empathy_account_settings_get_string (priv->settings, "account");
   if (!EMP_STR_EMPTY (account) &&
-      !g_str_has_suffix (account, FACEBOOK_SUFFIX))
+      !g_str_has_suffix (account, priv->jid_suffix))
     {
       gchar *tmp;
 
-      tmp = g_strdup_printf ("%s%s", account, FACEBOOK_SUFFIX);
+      tmp = g_strdup_printf ("%s%s", account, priv->jid_suffix);
 
       DEBUG ("Change account from '%s' to '%s'", account, tmp);
 
@@ -1010,17 +1399,23 @@ facebook_id_widget_changed_cb (GtkWidget *entry,
 }
 
 static gchar *
-remove_facebook_suffix (const gchar *str)
+remove_jid_suffix (EmpathyAccountWidget *self,
+    const gchar *str)
 {
-  if (!g_str_has_suffix (str, FACEBOOK_SUFFIX))
+  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+
+  g_assert (priv->jid_suffix != NULL);
+
+  if (!g_str_has_suffix (str, priv->jid_suffix))
     return g_strdup (str);
 
-  return g_strndup (str, strlen (str) - strlen (FACEBOOK_SUFFIX));
+  return g_strndup (str, strlen (str) - strlen (priv->jid_suffix));
 }
 
 static void
-setup_facebook_id_widget (EmpathyAccountWidget *self,
-    GtkWidget *widget)
+setup_id_widget_with_suffix (EmpathyAccountWidget *self,
+    GtkWidget *widget,
+    const gchar *suffix)
 {
   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
   const gchar *str = NULL;
@@ -1028,12 +1423,15 @@ setup_facebook_id_widget (EmpathyAccountWidget *self,
   g_object_set_data_full (G_OBJECT (widget), "param_name",
       g_strdup ("account"), g_free);
 
+  g_assert (priv->jid_suffix == NULL);
+  priv->jid_suffix = g_strdup (suffix);
+
   str = empathy_account_settings_get_string (priv->settings, "account");
   if (str != NULL)
     {
       gchar *tmp;
 
-      tmp = remove_facebook_suffix (str);
+      tmp = remove_jid_suffix (self, str);
       gtk_entry_set_text (GTK_ENTRY (widget), tmp);
       g_free (tmp);
     }
@@ -1041,7 +1439,29 @@ setup_facebook_id_widget (EmpathyAccountWidget *self,
   priv->param_account_widget = widget;
 
   g_signal_connect (widget, "changed",
-      G_CALLBACK (facebook_id_widget_changed_cb), self);
+      G_CALLBACK (suffix_id_widget_changed_cb), self);
+}
+
+static Service
+account_widget_get_service (EmpathyAccountWidget *self)
+{
+  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+  const gchar *icon_name, *service;
+
+  icon_name = empathy_account_settings_get_icon_name (priv->settings);
+  service = empathy_account_settings_get_service (priv->settings);
+
+  /* Previous versions of Empathy didn't set the Service property on Facebook
+   * and gtalk accounts, so we check using the icon name as well. */
+  if (!tp_strdiff (icon_name, "im-google-talk") ||
+      !tp_strdiff (service, "google-talk"))
+    return GTALK_SERVICE;
+
+  if (!tp_strdiff (icon_name, "im-facebook") ||
+      !tp_strdiff (service, "facebook"))
+    return FACEBOOK_SERVICE;
+
+  return NO_SERVICE;
 }
 
 static void
@@ -1053,15 +1473,18 @@ account_widget_build_jabber (EmpathyAccountWidget *self,
   GtkWidget *checkbutton_ssl;
   GtkWidget *label_id, *label_password;
   GtkWidget *label_id_create, *label_password_create;
-  GtkWidget *label_example_gtalk, *label_example_jabber, *label_example_fb;
-  gboolean is_gtalk, is_facebook;
+  GtkWidget *label_example_fb;
+  GtkWidget *label_example;
   GtkWidget *expander_advanced;
   GtkWidget *entry_id;
+  Service service;
+
+  service = account_widget_get_service (self);
 
-  is_gtalk = account_widget_is_gtalk (self);
-  is_facebook = account_widget_is_facebook (self);
+  empathy_account_settings_set_regex (priv->settings, "account",
+      ACCOUNT_REGEX_JABBER);
 
-  if (priv->simple && !is_gtalk && !is_facebook)
+  if (priv->simple && service == NO_SERVICE)
     {
       /* Simple widget for XMPP */
       self->ui_details->gui = empathy_builder_get_file (filename,
@@ -1086,8 +1509,11 @@ account_widget_build_jabber (EmpathyAccountWidget *self,
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_id_simple");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password_simple"));
     }
-  else if (priv->simple && is_gtalk)
+  else if (priv->simple && service == GTALK_SERVICE)
     {
       /* Simple widget for Google Talk */
       self->ui_details->gui = empathy_builder_get_file (filename,
@@ -1100,8 +1526,11 @@ account_widget_build_jabber (EmpathyAccountWidget *self,
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_id_g_simple");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password_g_simple"));
     }
-  else if (priv->simple && is_facebook)
+  else if (priv->simple && service == FACEBOOK_SERVICE)
     {
       /* Simple widget for Facebook */
       self->ui_details->gui = empathy_builder_get_file (filename,
@@ -1113,21 +1542,25 @@ account_widget_build_jabber (EmpathyAccountWidget *self,
           "entry_password_fb_simple", "password",
           NULL);
 
-      setup_facebook_id_widget (self, entry_id);
+      setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
 
       self->ui_details->default_focus = g_strdup ("entry_id_fb_simple");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password_fb_simple"));
     }
   else
     {
+      ServiceInfo info = services_infos[service];
+
       /* Full widget for XMPP, Google Talk and Facebook*/
       self->ui_details->gui = empathy_builder_get_file (filename,
-          "table_common_settings", &priv->table_common_settings,
+          "grid_common_settings", &priv->grid_common_settings,
           "vbox_jabber_settings", &self->ui_details->widget,
           "spinbutton_port", &spinbutton_port,
           "checkbutton_ssl", &checkbutton_ssl,
-          "label_username_example", &label_example_jabber,
-          "label_username_g_example", &label_example_gtalk,
           "label_username_f_example", &label_example_fb,
+          info.label_username_example, &label_example,
           "expander_advanced", &expander_advanced,
           "entry_id", &entry_id,
           "label_id", &label_id,
@@ -1144,13 +1577,13 @@ account_widget_build_jabber (EmpathyAccountWidget *self,
           "checkbutton_encryption", "require-encryption",
           NULL);
 
-      if (is_facebook)
+      if (service == FACEBOOK_SERVICE)
         {
           gtk_label_set_label (GTK_LABEL (label_id), _("Username:"));
 
           /* Facebook special case the entry ID widget to hide the
            * "@chat.facebook.com" part */
-          setup_facebook_id_widget (self, entry_id);
+          setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
         }
       else
         {
@@ -1160,21 +1593,30 @@ account_widget_build_jabber (EmpathyAccountWidget *self,
       self->ui_details->default_focus = g_strdup ("entry_id");
       priv->spinbutton_port = spinbutton_port;
 
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password"));
+
       g_signal_connect (checkbutton_ssl, "toggled",
           G_CALLBACK (account_widget_jabber_ssl_toggled_cb),
           self);
 
-      if (is_gtalk)
+      if (service == FACEBOOK_SERVICE)
         {
-          gtk_widget_hide (label_example_jabber);
-          gtk_widget_show (label_example_gtalk);
-        }
-      else if (is_facebook)
-        {
-          gtk_widget_hide (label_example_jabber);
-          gtk_widget_show (label_example_fb);
-          gtk_widget_hide (expander_advanced);
+          GtkContainer *parent;
+          GList *children;
+
+          /* Removing the label from list of focusable widgets */
+          parent = GTK_CONTAINER (gtk_widget_get_parent (label_example_fb));
+          children = gtk_container_get_children (parent);
+          children = g_list_remove (children, label_example_fb);
+          gtk_container_set_focus_chain (parent, children);
+          g_list_free (children);
         }
+
+      gtk_widget_show (label_example);
+
+      if (!info.show_advanced)
+        gtk_widget_hide (expander_advanced);
     }
 }
 
@@ -1185,6 +1627,9 @@ account_widget_build_icq (EmpathyAccountWidget *self,
   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
   GtkWidget *spinbutton_port;
 
+  empathy_account_settings_set_regex (priv->settings, "account",
+      ACCOUNT_REGEX_ICQ);
+
   if (priv->simple)
     {
       self->ui_details->gui = empathy_builder_get_file (filename,
@@ -1197,11 +1642,14 @@ account_widget_build_icq (EmpathyAccountWidget *self,
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_uin_simple");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password_simple"));
     }
   else
     {
       self->ui_details->gui = empathy_builder_get_file (filename,
-          "table_common_settings", &priv->table_common_settings,
+          "grid_common_settings", &priv->grid_common_settings,
           "vbox_icq_settings", &self->ui_details->widget,
           "spinbutton_port", &spinbutton_port,
           NULL);
@@ -1215,6 +1663,9 @@ account_widget_build_icq (EmpathyAccountWidget *self,
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_uin");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password"));
     }
 }
 
@@ -1237,11 +1688,14 @@ account_widget_build_aim (EmpathyAccountWidget *self,
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_screenname_simple");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password_simple"));
     }
   else
     {
       self->ui_details->gui = empathy_builder_get_file (filename,
-          "table_common_settings", &priv->table_common_settings,
+          "grid_common_settings", &priv->grid_common_settings,
           "vbox_aim_settings", &self->ui_details->widget,
           "spinbutton_port", &spinbutton_port,
           NULL);
@@ -1254,6 +1708,9 @@ account_widget_build_aim (EmpathyAccountWidget *self,
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_screenname");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password"));
     }
 }
 
@@ -1263,6 +1720,9 @@ account_widget_build_yahoo (EmpathyAccountWidget *self,
 {
   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
 
+  empathy_account_settings_set_regex (priv->settings, "account",
+      ACCOUNT_REGEX_YAHOO);
+
   if (priv->simple)
     {
       self->ui_details->gui = empathy_builder_get_file (filename,
@@ -1275,26 +1735,30 @@ account_widget_build_yahoo (EmpathyAccountWidget *self,
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_id_simple");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password_simple"));
     }
   else
     {
       self->ui_details->gui = empathy_builder_get_file (filename,
-          "table_common_settings", &priv->table_common_settings,
+          "grid_common_settings", &priv->grid_common_settings,
           "vbox_yahoo_settings", &self->ui_details->widget,
           NULL);
 
       empathy_account_widget_handle_params (self,
           "entry_id", "account",
           "entry_password", "password",
-          "entry_server", "server",
           "entry_locale", "room-list-locale",
           "entry_charset", "charset",
           "spinbutton_port", "port",
-          "checkbutton_yahoojp", "yahoojp",
           "checkbutton_ignore_invites", "ignore-invites",
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_id");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password"));
     }
 }
 
@@ -1316,11 +1780,14 @@ account_widget_build_groupwise (EmpathyAccountWidget *self,
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_id_simple");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password_simple"));
     }
   else
     {
       self->ui_details->gui = empathy_builder_get_file (filename,
-          "table_common_groupwise_settings", &priv->table_common_settings,
+          "grid_common_groupwise_settings", &priv->grid_common_settings,
           "vbox_groupwise_settings", &self->ui_details->widget,
           NULL);
 
@@ -1332,6 +1799,9 @@ account_widget_build_groupwise (EmpathyAccountWidget *self,
           NULL);
 
       self->ui_details->default_focus = g_strdup ("entry_id");
+
+      priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
+              self->ui_details->gui, "remember_password"));
     }
 }
 
@@ -1346,55 +1816,6 @@ account_widget_destroy_cb (GtkWidget *widget,
   g_object_unref (self);
 }
 
-static void
-empathy_account_widget_enabled_cb (TpAccount *account,
-      GParamSpec *spec,
-      gpointer user_data)
-{
-  EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
-  EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
-  gboolean enabled = tp_account_is_enabled (account);
-
-  if (priv->enabled_checkbox != NULL)
-    {
-#ifdef HAVE_MEEGO
-      mx_gtk_light_switch_set_active (
-          MX_GTK_LIGHT_SWITCH (priv->enabled_checkbox),
-          enabled);
-#else
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->enabled_checkbox),
-          enabled);
-#endif /* HAVE_MEEGO */
-    }
-}
-
-static void
-#ifdef HAVE_MEEGO
-account_widget_switch_flipped_cb (MxGtkLightSwitch *sw,
-    gboolean state,
-    gpointer user_data)
-#else
-account_widget_enabled_toggled_cb (GtkToggleButton *toggle_button,
-    gpointer user_data)
-#endif /* HAVE_MEEGO */
-{
-  EmpathyAccountWidgetPriv *priv = GET_PRIV (user_data);
-  TpAccount *account;
-#ifndef HAVE_MEEGO
-  gboolean state;
-
-  state = gtk_toggle_button_get_active (toggle_button);
-#endif /* HAVE_MEEGO */
-
-  account = empathy_account_settings_get_account (priv->settings);
-
-  /* Enable the account according to the value of the "Enabled" checkbox */
-  /* workaround to keep widget alive during async call */
-  g_object_ref (user_data);
-  tp_account_set_enabled_async (account, state,
-      account_widget_account_enabled_cb, user_data);
-}
-
 void
 empathy_account_widget_set_other_accounts_exist (EmpathyAccountWidget *self,
     gboolean others_exist)
@@ -1530,7 +1951,7 @@ account_manager_ready_cb (GObject *source_object,
   GError *error = NULL;
   TpConnectionPresenceType state;
 
-  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);
@@ -1552,74 +1973,6 @@ out:
   { #cm, #proto, "empathy-account-widget-"#proto".ui", \
     account_widget_build_##proto }
 
-static void
-add_enable_checkbox (EmpathyAccountWidget *self,
-    TpAccount *account)
-{
-  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
-#ifdef HAVE_MEEGO
-  GtkWidget *w;
-#else
-  GtkWidget *vbox = self->ui_details->widget;
-#endif /* HAVE_MEEGO */
-  guint nb_rows, nb_columns;
-  gboolean is_enabled;
-
-  /* handle the "Enabled" checkbox. We only add it when modifying an account */
-  if (priv->creating_account || priv->table_common_settings == NULL)
-    return;
-
-  is_enabled = tp_account_is_enabled (account);
-
-#ifdef HAVE_MEEGO
-  w = gtk_label_new (_("Account:"));
-  gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5);
-
-  priv->enabled_checkbox = mx_gtk_light_switch_new ();
-
-  mx_gtk_light_switch_set_active (
-      MX_GTK_LIGHT_SWITCH (priv->enabled_checkbox), is_enabled);
-
-  gtk_widget_show (w);
-#else
-  priv->enabled_checkbox =
-      gtk_check_button_new_with_mnemonic (_("_Enabled"));
-
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->enabled_checkbox),
-      is_enabled);
-#endif /* HAVE_MEEGO */
-
-  g_object_get (priv->table_common_settings, "n-rows", &nb_rows,
-      "n-columns", &nb_columns, NULL);
-
-  gtk_table_resize (GTK_TABLE (priv->table_common_settings), ++nb_rows,
-      nb_columns);
-
-#ifdef HAVE_MEEGO
-  gtk_table_attach (GTK_TABLE (priv->table_common_settings),
-      w,
-      0, 1, nb_rows - 1, nb_rows,
-      GTK_FILL, 0, 0, 0);
-  gtk_table_attach (GTK_TABLE (priv->table_common_settings),
-      priv->enabled_checkbox,
-      1, nb_columns, nb_rows - 1, nb_rows,
-      GTK_EXPAND | GTK_FILL, 0, 0, 0);
-#else
-  gtk_box_pack_start (GTK_BOX (vbox), priv->enabled_checkbox, FALSE, FALSE, 0);
-  gtk_box_reorder_child (GTK_BOX (vbox), priv->enabled_checkbox, 0);
-#endif /* HAVE_MEEGO */
-
-  gtk_widget_show (priv->enabled_checkbox);
-
-#ifdef HAVE_MEEGO
-  g_signal_connect (G_OBJECT (priv->enabled_checkbox), "switch-flipped",
-      G_CALLBACK (account_widget_switch_flipped_cb), self);
-#else
-  g_signal_connect (G_OBJECT (priv->enabled_checkbox), "toggled",
-      G_CALLBACK (account_widget_enabled_toggled_cb), self);
-#endif /* HAVE_MEEGO */
-}
-
 #ifndef HAVE_MEEGO
 /* Meego doesn't support registration */
 static void
@@ -1641,7 +1994,7 @@ add_register_buttons (EmpathyAccountWidget *self,
   if (!tp_connection_manager_protocol_can_register (protocol))
     return;
 
-  if (account_widget_is_gtalk (self) || account_widget_is_facebook (self))
+  if (account_widget_get_service (self) != NO_SERVICE)
     return;
 
   if (priv->simple)
@@ -1662,13 +2015,55 @@ add_register_buttons (EmpathyAccountWidget *self,
 }
 #endif /* HAVE_MEEGO */
 
+static void
+remember_password_toggled_cb (GtkToggleButton *button,
+    EmpathyAccountWidget *self)
+{
+  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+
+  if (gtk_toggle_button_get_active (button))
+    {
+      gtk_widget_set_sensitive (priv->param_password_widget, TRUE);
+    }
+  else
+    {
+      gtk_widget_set_sensitive (priv->param_password_widget, FALSE);
+      gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), "");
+      empathy_account_settings_unset (priv->settings, "password");
+    }
+}
+
+static void
+account_settings_password_retrieved_cb (GObject *object,
+    gpointer user_data)
+{
+  EmpathyAccountWidget *self = user_data;
+  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+  const gchar *password = empathy_account_settings_get_string (
+      priv->settings, "password");
+
+  if (password != NULL)
+    {
+      /* We have to do this so that when we call gtk_entry_set_text,
+       * the ::changed callback doesn't think the user made the
+       * change. */
+      priv->automatic_change = TRUE;
+      gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), password);
+      priv->automatic_change = FALSE;
+    }
+
+  gtk_toggle_button_set_active (
+      GTK_TOGGLE_BUTTON (priv->remember_password_widget),
+      !EMP_STR_EMPTY (password));
+}
+
 static void
 do_constructed (GObject *obj)
 {
   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
   TpAccount *account;
-  const gchar *protocol, *cm_name;
+  TpStorageRestrictionFlags storage_restrictions;
   const gchar *display_name, *default_display_name;
   guint i = 0;
   struct {
@@ -1689,31 +2084,50 @@ do_constructed (GObject *obj)
     WIDGET (sofiasip, sip),
   };
 
-  cm_name = empathy_account_settings_get_cm (priv->settings);
-  protocol = empathy_account_settings_get_protocol (priv->settings);
+  account = empathy_account_settings_get_account (priv->settings);
+
+  if (account != NULL)
+    storage_restrictions = tp_account_get_storage_restrictions (account);
+  else
+    storage_restrictions = 0;
 
-  for (i = 0 ; i < G_N_ELEMENTS (widgets); i++)
+  /* Empathy can only edit accounts without the Cannot_Set_Parameters flag */
+  if (storage_restrictions & TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS)
     {
-      if (!tp_strdiff (widgets[i].cm_name, cm_name) &&
-          !tp_strdiff (widgets[i].protocol, protocol))
+      DEBUG ("Account is provided by an external storage provider");
+
+      account_widget_build_external (self, priv->settings);
+    }
+  else
+    {
+      const gchar *protocol, *cm_name;
+
+      cm_name = empathy_account_settings_get_cm (priv->settings);
+      protocol = empathy_account_settings_get_protocol (priv->settings);
+
+      for (i = 0 ; i < G_N_ELEMENTS (widgets); i++)
         {
-          gchar *filename;
+          if (!tp_strdiff (widgets[i].cm_name, cm_name) &&
+              !tp_strdiff (widgets[i].protocol, protocol))
+            {
+              gchar *filename;
 
-          filename = empathy_file_lookup (widgets[i].file,
-              "libempathy-gtk");
-          widgets[i].func (self, filename);
-          g_free (filename);
+              filename = empathy_file_lookup (widgets[i].file,
+                  "libempathy-gtk");
+              widgets[i].func (self, filename);
+              g_free (filename);
 
-          break;
+              break;
+            }
         }
-    }
 
-  if (i == G_N_ELEMENTS (widgets))
-    {
-      gchar *filename = empathy_file_lookup (
-          "empathy-account-widget-generic.ui", "libempathy-gtk");
-      account_widget_build_generic (self, filename);
-      g_free (filename);
+      if (i == G_N_ELEMENTS (widgets))
+        {
+          gchar *filename = empathy_file_lookup (
+              "empathy-account-widget-generic.ui", "libempathy-gtk");
+          account_widget_build_generic (self, filename);
+          g_free (filename);
+        }
     }
 
   /* handle default focus */
@@ -1728,19 +2142,59 @@ do_constructed (GObject *obj)
           NULL);
     }
 
+  /* remember password */
+  if (priv->param_password_widget != NULL
+      && priv->remember_password_widget != NULL
+      && empathy_account_settings_supports_sasl (priv->settings))
+    {
+      if (priv->simple)
+        {
+          gtk_toggle_button_set_active (
+              GTK_TOGGLE_BUTTON (priv->remember_password_widget), TRUE);
+        }
+      else
+        {
+          gtk_toggle_button_set_active (
+              GTK_TOGGLE_BUTTON (priv->remember_password_widget),
+              !EMP_STR_EMPTY (empathy_account_settings_get_string (
+                      priv->settings, "password")));
+
+          /* The password might not have been retrieved from the
+           * keyring yet. We should update the remember password
+           * toggle button and the password entry when/if it is. */
+          tp_g_signal_connect_object (priv->settings, "password-retrieved",
+              G_CALLBACK (account_settings_password_retrieved_cb), self, 0);
+        }
+
+      g_signal_connect (priv->remember_password_widget, "toggled",
+          G_CALLBACK (remember_password_toggled_cb), self);
+
+      remember_password_toggled_cb (
+          GTK_TOGGLE_BUTTON (priv->remember_password_widget), self);
+    }
+  else if (priv->remember_password_widget != NULL
+      && !empathy_account_settings_supports_sasl (priv->settings))
+    {
+      gtk_widget_set_visible (priv->remember_password_widget, FALSE);
+    }
+
   /* dup and init the account-manager */
   priv->account_manager = tp_account_manager_dup ();
 
   g_object_ref (self);
-  tp_account_manager_prepare_async (priv->account_manager, NULL,
+  tp_proxy_prepare_async (priv->account_manager, NULL,
       account_manager_ready_cb, self);
 
   /* handle apply and cancel button */
-  if (!priv->simple)
+  if (!priv->simple &&
+      !(storage_restrictions &
+        TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS))
     {
-      GtkWidget *hbox = gtk_hbox_new (TRUE, 3);
+      GtkWidget *hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
       GtkWidget *image;
 
+      gtk_box_set_homogeneous (hbox, TRUE);
+
       /*  We can't use the stock button as its accelerator ('C') clashes with
        *  the Close button. */
       priv->cancel_button = gtk_button_new ();
@@ -1785,31 +2239,26 @@ do_constructed (GObject *obj)
         account_widget_set_control_buttons_sensitivity (self, FALSE);
     }
 
-  account = empathy_account_settings_get_account (priv->settings);
-
-  if (account != NULL)
-    {
-      g_signal_connect (account, "notify::enabled",
-          G_CALLBACK (empathy_account_widget_enabled_cb), self);
-    }
-
 #ifndef HAVE_MEEGO
   add_register_buttons (self, account);
 #endif /* HAVE_MEEGO */
-  add_enable_checkbox (self, account);
 
   /* hook up to widget destruction to unref ourselves */
   g_signal_connect (self->ui_details->widget, "destroy",
       G_CALLBACK (account_widget_destroy_cb), self);
 
-  empathy_builder_unref_and_keep_widget (self->ui_details->gui,
-      self->ui_details->widget);
-  self->ui_details->gui = NULL;
+  if (self->ui_details->gui != NULL)
+    {
+      empathy_builder_unref_and_keep_widget (self->ui_details->gui,
+          self->ui_details->widget);
+      self->ui_details->gui = NULL;
+    }
 
   display_name = empathy_account_settings_get_display_name (priv->settings);
   default_display_name = empathy_account_widget_get_default_display_name (self);
 
-  if (tp_strdiff (display_name, default_display_name))
+  if (tp_strdiff (display_name, default_display_name) &&
+      !priv->creating_account)
     {
       /* The display name of the account is not the one that we'd assign by
        * default; assume that the user changed it manually */
@@ -1830,15 +2279,6 @@ do_dispose (GObject *obj)
 
   if (priv->settings != NULL)
     {
-      TpAccount *account;
-      account = empathy_account_settings_get_account (priv->settings);
-
-      if (account != NULL)
-        {
-          g_signal_handlers_disconnect_by_func (account,
-              empathy_account_widget_enabled_cb, self);
-        }
-
       g_object_unref (priv->settings);
       priv->settings = NULL;
     }
@@ -1857,10 +2297,13 @@ static void
 do_finalize (GObject *obj)
 {
   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
+  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
 
   g_free (self->ui_details->default_focus);
   g_slice_free (EmpathyAccountWidgetUIDetails, self->ui_details);
 
+  g_free (priv->jid_suffix);
+
   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize != NULL)
     G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize (obj);
 }
@@ -1913,7 +2356,7 @@ empathy_account_widget_class_init (EmpathyAccountWidgetClass *klass)
   signals[HANDLE_APPLY] =
     g_signal_new ("handle-apply", G_TYPE_FROM_CLASS (klass),
         G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-        g_cclosure_marshal_VOID__BOOLEAN,
+        g_cclosure_marshal_generic,
         G_TYPE_NONE,
         1, G_TYPE_BOOLEAN);
 
@@ -1921,14 +2364,14 @@ empathy_account_widget_class_init (EmpathyAccountWidgetClass *klass)
   signals[ACCOUNT_CREATED] =
       g_signal_new ("account-created", G_TYPE_FROM_CLASS (klass),
           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-          g_cclosure_marshal_VOID__OBJECT,
+          g_cclosure_marshal_generic,
           G_TYPE_NONE,
           1, G_TYPE_OBJECT);
 
   signals[CANCELLED] =
       g_signal_new ("cancelled", G_TYPE_FROM_CLASS (klass),
           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-          g_cclosure_marshal_VOID__VOID,
+          g_cclosure_marshal_generic,
           G_TYPE_NONE,
           0);
 
@@ -2011,9 +2454,11 @@ empathy_account_widget_get_default_display_name (EmpathyAccountWidget *self)
   const gchar *login_id;
   const gchar *protocol, *p;
   gchar *default_display_name;
+  Service service;
 
   login_id = empathy_account_settings_get_string (priv->settings, "account");
   protocol = empathy_account_settings_get_protocol (priv->settings);
+  service = account_widget_get_service (self);
 
   if (login_id != NULL)
     {
@@ -2034,11 +2479,11 @@ empathy_account_widget_get_default_display_name (EmpathyAccountWidget *self)
           default_display_name = g_strdup_printf (_("%1$s on %2$s"),
               login_id, empathy_irc_network_get_name (network));
         }
-      else if (account_widget_is_facebook (self))
+      else if (service == FACEBOOK_SERVICE && priv->jid_suffix != NULL)
         {
           gchar *tmp;
 
-          tmp = remove_facebook_suffix (login_id);
+          tmp = remove_jid_suffix (self, login_id);
           default_display_name = g_strdup_printf ("Facebook (%s)", tmp);
           g_free (tmp);
         }
@@ -2100,3 +2545,11 @@ empathy_account_widget_set_password_param (EmpathyAccountWidget *self,
 
   gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), account);
 }
+
+EmpathyAccountSettings *
+empathy_account_widget_get_settings (EmpathyAccountWidget *self)
+{
+  EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+
+  return priv->settings;
+}