]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-account-widget.c
Correct grouping in the hostname regexp
[empathy.git] / libempathy-gtk / empathy-account-widget.c
1 /*
2  * Copyright (C) 2006-2007 Imendio AB
3  * Copyright (C) 2007-2009 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301  USA
19  *
20  * Authors: Xavier Claessens <xclaesse@gmail.com>
21  *          Martyn Russell <martyn@imendio.com>
22  *          Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
23  *          Jonathan Tellier <jonathan.tellier@gmail.com>
24  */
25
26 #include <config.h>
27
28 #include <string.h>
29
30 #include <gtk/gtk.h>
31 #include <glib/gi18n-lib.h>
32
33 #include <gio/gdesktopappinfo.h>
34
35 #include <libempathy/empathy-utils.h>
36
37 #include <telepathy-glib/account.h>
38 #include <telepathy-glib/account-manager.h>
39 #include <telepathy-glib/connection-manager.h>
40 #include <telepathy-glib/util.h>
41 #include <dbus/dbus-protocol.h>
42
43 #include "empathy-account-widget.h"
44 #include "empathy-account-widget-private.h"
45 #include "empathy-account-widget-sip.h"
46 #include "empathy-account-widget-irc.h"
47 #include "empathy-ui-utils.h"
48
49 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
50 #include <libempathy/empathy-debug.h>
51
52 G_DEFINE_TYPE (EmpathyAccountWidget, empathy_account_widget, G_TYPE_OBJECT)
53
54 typedef enum
55 {
56   NO_SERVICE = 0,
57   GTALK_SERVICE,
58   FACEBOOK_SERVICE,
59   N_SERVICES
60 } Service;
61
62 typedef struct
63 {
64   const gchar *label_username_example;
65   gboolean show_advanced;
66 } ServiceInfo;
67
68 static ServiceInfo services_infos[N_SERVICES] = {
69     { "label_username_example", TRUE },
70     { "label_username_g_example", FALSE },
71     { "label_username_f_example", FALSE },
72 };
73
74 typedef struct {
75   EmpathyAccountSettings *settings;
76
77   GtkWidget *table_common_settings;
78   GtkWidget *apply_button;
79   GtkWidget *cancel_button;
80   GtkWidget *entry_password;
81   GtkWidget *spinbutton_port;
82   GtkWidget *radiobutton_reuse;
83
84   gboolean simple;
85
86   gboolean contains_pending_changes;
87
88   /* An EmpathyAccountWidget can be used to either create an account or
89    * modify it. When we are creating an account, this member is set to TRUE */
90   gboolean creating_account;
91
92   /* whether there are any other real accounts. Necessary so we know whether
93    * it's safe to dismiss this widget in some cases (eg, whether the Cancel
94    * button should be sensitive) */
95   gboolean other_accounts_exist;
96
97   /* if TRUE, the GTK+ destroy signal has been fired and so the widgets
98    * embedded in this account widget can't be used any more
99    * workaround because some async callbacks can be called after the
100    * widget has been destroyed */
101   gboolean destroyed;
102
103   TpAccountManager *account_manager;
104
105   GtkWidget *param_account_widget;
106   GtkWidget *param_password_widget;
107
108   gboolean automatic_change;
109   GtkWidget *remember_password_widget;
110
111   /* Used only for IRC accounts */
112   EmpathyIrcNetworkChooser *irc_network_chooser;
113
114   /* Used for 'special' XMPP account having a service associated ensuring that
115    * JIDs have a specific suffix; such as Facebook for example */
116   gchar *jid_suffix;
117
118   gboolean dispose_run;
119 } EmpathyAccountWidgetPriv;
120
121 enum {
122   PROP_PROTOCOL = 1,
123   PROP_SETTINGS,
124   PROP_SIMPLE,
125   PROP_CREATING_ACCOUNT,
126   PROP_OTHER_ACCOUNTS_EXIST,
127 };
128
129 enum {
130   HANDLE_APPLY,
131   ACCOUNT_CREATED,
132   CANCELLED,
133   LAST_SIGNAL
134 };
135
136 static void account_widget_apply_and_log_in (EmpathyAccountWidget *);
137
138 enum {
139   RESPONSE_LAUNCH
140 };
141
142 static guint signals[LAST_SIGNAL] = { 0 };
143
144 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountWidget)
145 #define CHANGED_TIMEOUT 300
146
147 #define DIGIT             "0-9"
148 #define DIGITS            "(["DIGIT"]+)"
149 #define ALPHA             "a-zA-Z"
150 #define ALPHAS            "(["ALPHA"]+)"
151 #define ALPHADIGIT        ALPHA DIGIT
152 #define ALPHADIGITS       "(["ALPHADIGIT"]+)"
153 #define ALPHADIGITDASH    ALPHA DIGIT "-"
154 #define ALPHADIGITDASHS   "(["ALPHADIGITDASH"]*)"
155
156 #define HOSTNUMBER        "("DIGITS"\\."DIGITS"\\."DIGITS"\\."DIGITS")"
157 #define TOPLABEL          "("ALPHAS \
158                             "| (["ALPHA"]"ALPHADIGITDASHS "["ALPHADIGIT"]))"
159 #define DOMAINLABEL       "("ALPHADIGITS"|(["ALPHADIGIT"]" ALPHADIGITDASHS \
160                                        "["ALPHADIGIT"]))"
161 #define HOSTNAME          "((" DOMAINLABEL "\\.)+" TOPLABEL ")"
162 /* Based on http://www.ietf.org/rfc/rfc1738.txt (section 5) */
163 #define HOST              "("HOSTNAME "|" HOSTNUMBER")"
164 /* Based on http://www.ietf.org/rfc/rfc0822.txt (appendix D) */
165 #define EMAIL_LOCALPART   "([^\\(\\)<>@,;:\\\\\"\\[\\]\\s]+)"
166
167 /* UIN is digital according to the unofficial specification:
168  * http://iserverd.khstu.ru/docum_ext/icqv5.html#CTS
169  * 5 digits minimum according to http://en.wikipedia.org/wiki/ICQ#UIN
170  * According to an user, we can also provide an email address instead of the
171  * ICQ UIN. */
172 #define ICQ_USER_NAME     "((["DIGIT"]{5,})|"EMAIL_LOCALPART"@"HOST")"
173
174 /* Based on http://www.ietf.org/rfc/rfc2812.txt (section 2.3.1) */
175 #define IRC_SPECIAL       "_\\[\\]{}\\\\|`^"
176 #define IRC_NICK_NAME     "(["ALPHA IRC_SPECIAL"]["ALPHADIGITDASH IRC_SPECIAL"]*)"
177 /*   user       =  1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF )
178  *                ; any octet except NUL, CR, LF, " " and "@"
179  *
180  * so technically, like so many other places in IRC, we should be using arrays
181  * of bytes here rather than UTF-8 strings. Life: too short. In practice this
182  * will always be ASCII.
183  */
184 #define IRC_USER_NAME     "([^\r\n@ ])+"
185
186 /* Based on http://www.ietf.org/rfc/rfc4622.txt (section 2.2)
187  * We just exclude invalid characters to avoid ucschars and other redundant
188  * complexity */
189 #define JABBER_USER_NAME  "([^@:'\"<>&\\s]+)"
190 /* ID is an email according to the unofficial specification:
191  * http://www.hypothetic.org/docs/msn/general/names.php */
192 #define MSN_USER_NAME     EMAIL_LOCALPART
193 /* Based on the official help:
194  * http://help.yahoo.com/l/us/yahoo/edit/registration/edit-01.html
195  * Looks like an email address can be used as well (bgo #655959)
196  * */
197 #define YAHOO_USER_NAME   "(["ALPHA"]["ALPHADIGIT"_\\.]{3,31})|("EMAIL_LOCALPART"@"HOST")"
198
199 #define ACCOUNT_REGEX_ICQ      "^"ICQ_USER_NAME"$"
200 #define ACCOUNT_REGEX_IRC      "^"IRC_NICK_NAME"$"
201 #define USERNAME_REGEX_IRC     "^"IRC_USER_NAME"$"
202 #define ACCOUNT_REGEX_JABBER   "^"JABBER_USER_NAME"@"HOST"$"
203 #define ACCOUNT_REGEX_MSN      "^"MSN_USER_NAME"@"HOST"$"
204 #define ACCOUNT_REGEX_YAHOO    "^"YAHOO_USER_NAME"$"
205
206 static void
207 account_widget_set_control_buttons_sensitivity (EmpathyAccountWidget *self,
208     gboolean sensitive)
209 {
210   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
211
212   if (!priv->simple)
213     {
214       /* we hit this case because of the 'other-accounts-exist' property handler
215        * being called during init (before constructed()) */
216       if (priv->apply_button == NULL || priv->cancel_button == NULL)
217         return;
218
219       gtk_widget_set_sensitive (priv->apply_button, sensitive);
220       gtk_widget_set_sensitive (priv->cancel_button,
221           (sensitive || priv->creating_account) && priv->other_accounts_exist);
222
223       if (sensitive)
224         {
225           /* We can't grab default if the widget hasn't be packed in a
226            * window */
227           GtkWidget *window;
228
229           window = gtk_widget_get_toplevel (priv->apply_button);
230           if (window != NULL &&
231               gtk_widget_is_toplevel (window))
232             {
233               gtk_widget_set_can_default (priv->apply_button, TRUE);
234               gtk_widget_grab_default (priv->apply_button);
235             }
236         }
237     }
238 }
239
240 static void
241 account_widget_set_entry_highlighting (GtkEntry *entry,
242     gboolean highlight)
243 {
244   g_return_if_fail (GTK_IS_ENTRY (entry));
245
246   if (highlight)
247     {
248       GtkStyleContext *style;
249       GdkRGBA color;
250
251       style = gtk_widget_get_style_context (GTK_WIDGET (entry));
252       gtk_style_context_get_background_color (style, GTK_STATE_FLAG_SELECTED,
253           &color);
254
255       /* Here we take the current theme colour and add it to
256        * the colour for white and average the two. This
257        * gives a colour which is inline with the theme but
258        * slightly whiter.
259        */
260       empathy_make_color_whiter (&color);
261
262       gtk_widget_override_background_color (GTK_WIDGET (entry), 0, &color);
263     }
264   else
265     {
266       gtk_widget_override_background_color (GTK_WIDGET (entry), 0, NULL);
267     }
268 }
269
270 static void
271 account_widget_handle_control_buttons_sensitivity (EmpathyAccountWidget *self)
272 {
273   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
274   gboolean is_valid;
275
276   is_valid = empathy_account_settings_is_valid (priv->settings);
277
278   if (!priv->simple)
279       account_widget_set_control_buttons_sensitivity (self, is_valid);
280
281   g_signal_emit (self, signals[HANDLE_APPLY], 0, is_valid);
282 }
283
284 static void
285 account_widget_entry_changed_common (EmpathyAccountWidget *self,
286     GtkEntry *entry, gboolean focus)
287 {
288   const gchar *str;
289   const gchar *param_name;
290   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
291   gboolean prev_status;
292   gboolean curr_status;
293
294   str = gtk_entry_get_text (entry);
295   param_name = g_object_get_data (G_OBJECT (entry), "param_name");
296   prev_status = empathy_account_settings_parameter_is_valid (priv->settings,
297                                                              param_name);
298
299   if (EMP_STR_EMPTY (str))
300     {
301       const gchar *value = NULL;
302
303       empathy_account_settings_unset (priv->settings, param_name);
304
305       if (focus)
306         {
307           value = empathy_account_settings_get_string (priv->settings,
308               param_name);
309           DEBUG ("Unset %s and restore to %s", param_name, value);
310           gtk_entry_set_text (entry, value ? value : "");
311         }
312     }
313   else
314     {
315       DEBUG ("Setting %s to %s", param_name,
316           tp_strdiff (param_name, "password") ? str : "***");
317       empathy_account_settings_set_string (priv->settings, param_name, str);
318     }
319
320   curr_status = empathy_account_settings_parameter_is_valid (priv->settings,
321                                                              param_name);
322   if (curr_status != prev_status)
323     account_widget_set_entry_highlighting (entry, !curr_status);
324 }
325
326 static void
327 account_widget_entry_changed_cb (GtkEditable *entry,
328     EmpathyAccountWidget *self)
329 {
330   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
331
332   if (priv->automatic_change)
333     return;
334
335   account_widget_entry_changed_common (self, GTK_ENTRY (entry), FALSE);
336   empathy_account_widget_changed (self);
337 }
338
339 static void
340 account_widget_entry_map_cb (GtkEntry *entry,
341     EmpathyAccountWidget *self)
342 {
343   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
344   const gchar *param_name;
345   gboolean is_valid;
346
347   /* need to initialize input highlighting */
348   param_name = g_object_get_data (G_OBJECT (entry), "param_name");
349   is_valid = empathy_account_settings_parameter_is_valid (priv->settings,
350                                                           param_name);
351   account_widget_set_entry_highlighting (entry, !is_valid);
352 }
353
354 static void
355 account_widget_int_changed_cb (GtkWidget *widget,
356     EmpathyAccountWidget *self)
357 {
358   const gchar *param_name;
359   gint value;
360   const gchar *signature;
361   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
362
363   value = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
364   param_name = g_object_get_data (G_OBJECT (widget), "param_name");
365
366   signature = empathy_account_settings_get_dbus_signature (priv->settings,
367     param_name);
368   g_return_if_fail (signature != NULL);
369
370   DEBUG ("Setting %s to %d", param_name, value);
371
372   switch ((int)*signature)
373     {
374     case DBUS_TYPE_INT16:
375     case DBUS_TYPE_INT32:
376       empathy_account_settings_set_int32 (priv->settings, param_name, value);
377       break;
378     case DBUS_TYPE_INT64:
379       empathy_account_settings_set_int64 (priv->settings, param_name, value);
380       break;
381     case DBUS_TYPE_UINT16:
382     case DBUS_TYPE_UINT32:
383       empathy_account_settings_set_uint32 (priv->settings, param_name, value);
384       break;
385     case DBUS_TYPE_UINT64:
386       empathy_account_settings_set_uint64 (priv->settings, param_name, value);
387       break;
388     default:
389       g_return_if_reached ();
390     }
391
392   empathy_account_widget_changed (self);
393 }
394
395 static void
396 account_widget_checkbutton_toggled_cb (GtkWidget *widget,
397     EmpathyAccountWidget *self)
398 {
399   gboolean     value;
400   gboolean     default_value;
401   const gchar *param_name;
402   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
403
404   value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
405   param_name = g_object_get_data (G_OBJECT (widget), "param_name");
406
407   /* FIXME: This is ugly! checkbox don't have a "not-set" value so we
408    * always unset the param and set the value if different from the
409    * default value. */
410   empathy_account_settings_unset (priv->settings, param_name);
411   default_value = empathy_account_settings_get_boolean (priv->settings,
412       param_name);
413
414   if (default_value == value)
415     {
416       DEBUG ("Unset %s and restore to %d", param_name, default_value);
417     }
418   else
419     {
420       DEBUG ("Setting %s to %d", param_name, value);
421       empathy_account_settings_set_boolean (priv->settings, param_name, value);
422     }
423
424   empathy_account_widget_changed (self);
425 }
426
427 static void
428 account_widget_jabber_ssl_toggled_cb (GtkWidget *checkbutton_ssl,
429     EmpathyAccountWidget *self)
430 {
431   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
432   gboolean   value;
433   gint32       port = 0;
434
435   value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton_ssl));
436   port = empathy_account_settings_get_uint32 (priv->settings, "port");
437
438   if (value)
439     {
440       if (port == 5222 || port == 0)
441         port = 5223;
442     }
443   else
444     {
445       if (port == 5223 || port == 0)
446         port = 5222;
447     }
448
449   gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->spinbutton_port), port);
450
451   priv->contains_pending_changes = TRUE;
452 }
453
454 static void
455 account_widget_combobox_changed_cb (GtkWidget *widget,
456     EmpathyAccountWidget *self)
457 {
458   GtkTreeIter iter;
459   GtkTreeModel *model;
460   const gchar *value;
461   const GValue *v;
462   const gchar *default_value = NULL;
463   const gchar *param_name;
464   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
465
466   if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter))
467     return;
468
469   model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
470   /* the param value is stored in the first column */
471   gtk_tree_model_get (model, &iter, 0, &value, -1);
472
473   param_name = g_object_get_data (G_OBJECT (widget), "param_name");
474
475   v = empathy_account_settings_get_default (priv->settings, param_name);
476   if (v != NULL)
477     default_value = g_value_get_string (v);
478
479   if (!tp_strdiff (value, default_value))
480     {
481       DEBUG ("Unset %s and restore to %s", param_name, default_value);
482       empathy_account_settings_unset (priv->settings, param_name);
483     }
484   else
485     {
486       DEBUG ("Setting %s to %s", param_name, value);
487       empathy_account_settings_set_string (priv->settings, param_name, value);
488     }
489
490   empathy_account_widget_changed (self);
491 }
492
493 static void
494 clear_icon_released_cb (GtkEntry *entry,
495     GtkEntryIconPosition icon_pos,
496     GdkEvent *event,
497     EmpathyAccountWidget *self)
498 {
499   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
500   const gchar *param_name;
501
502   param_name = g_object_get_data (G_OBJECT (entry), "param_name");
503
504   DEBUG ("Unset %s", param_name);
505   empathy_account_settings_unset (priv->settings, param_name);
506   gtk_entry_set_text (entry, "");
507
508   empathy_account_widget_changed (self);
509 }
510
511 static void
512 password_entry_changed_cb (GtkEditable *entry,
513     EmpathyAccountWidget *self)
514 {
515   const gchar *str;
516
517   str = gtk_entry_get_text (GTK_ENTRY (entry));
518
519   gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
520       GTK_ENTRY_ICON_SECONDARY, !EMP_STR_EMPTY (str));
521 }
522
523 static void
524 password_entry_activated_cb (GtkEntry *entry,
525     EmpathyAccountWidget *self)
526 {
527     EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
528
529     if (gtk_widget_get_sensitive (priv->apply_button))
530         account_widget_apply_and_log_in (self);
531 }
532
533 static void
534 account_entry_activated_cb (GtkEntry *entry,
535     EmpathyAccountWidget *self)
536 {
537     EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
538
539     if (gtk_widget_get_sensitive (priv->apply_button))
540         account_widget_apply_and_log_in (self);
541 }
542
543 void
544 empathy_account_widget_setup_widget (EmpathyAccountWidget *self,
545     GtkWidget *widget,
546     const gchar *param_name)
547 {
548   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
549
550   g_object_set_data_full (G_OBJECT (widget), "param_name",
551       g_strdup (param_name), g_free);
552
553   if (GTK_IS_SPIN_BUTTON (widget))
554     {
555       gint value = 0;
556       const gchar *signature;
557
558       signature = empathy_account_settings_get_dbus_signature (priv->settings,
559           param_name);
560       g_return_if_fail (signature != NULL);
561
562       switch ((int)*signature)
563         {
564           case DBUS_TYPE_INT16:
565           case DBUS_TYPE_INT32:
566             value = empathy_account_settings_get_int32 (priv->settings,
567               param_name);
568             break;
569           case DBUS_TYPE_INT64:
570             value = empathy_account_settings_get_int64 (priv->settings,
571               param_name);
572             break;
573           case DBUS_TYPE_UINT16:
574           case DBUS_TYPE_UINT32:
575             value = empathy_account_settings_get_uint32 (priv->settings,
576               param_name);
577             break;
578           case DBUS_TYPE_UINT64:
579             value = empathy_account_settings_get_uint64 (priv->settings,
580                 param_name);
581             break;
582           default:
583             g_return_if_reached ();
584         }
585
586       gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value);
587
588       g_signal_connect (widget, "value-changed",
589           G_CALLBACK (account_widget_int_changed_cb),
590           self);
591     }
592   else if (GTK_IS_ENTRY (widget))
593     {
594       const gchar *str = NULL;
595
596       str = empathy_account_settings_get_string (priv->settings, param_name);
597       gtk_entry_set_text (GTK_ENTRY (widget), str ? str : "");
598
599       if (!tp_strdiff (param_name, "account"))
600         priv->param_account_widget = widget;
601       else if (!tp_strdiff (param_name, "password"))
602         priv->param_password_widget = widget;
603
604       if (strstr (param_name, "password"))
605         {
606           gtk_entry_set_visibility (GTK_ENTRY (widget), FALSE);
607
608           /* Add 'clear' icon */
609           gtk_entry_set_icon_from_stock (GTK_ENTRY (widget),
610               GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
611
612           gtk_entry_set_icon_sensitive (GTK_ENTRY (widget),
613               GTK_ENTRY_ICON_SECONDARY, !EMP_STR_EMPTY (str));
614
615           g_signal_connect (widget, "icon-release",
616               G_CALLBACK (clear_icon_released_cb), self);
617           g_signal_connect (widget, "changed",
618               G_CALLBACK (password_entry_changed_cb), self);
619           g_signal_connect (widget, "activate",
620               G_CALLBACK (password_entry_activated_cb), self);
621         }
622       else if (strstr (param_name, "account"))
623         g_signal_connect (widget, "activate",
624             G_CALLBACK (account_entry_activated_cb), self);
625
626
627       g_signal_connect (widget, "changed",
628           G_CALLBACK (account_widget_entry_changed_cb), self);
629       g_signal_connect (widget, "map",
630           G_CALLBACK (account_widget_entry_map_cb), self);
631     }
632   else if (GTK_IS_TOGGLE_BUTTON (widget))
633     {
634       gboolean value = FALSE;
635
636       value = empathy_account_settings_get_boolean (priv->settings,
637           param_name);
638       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
639
640       g_signal_connect (widget, "toggled",
641           G_CALLBACK (account_widget_checkbutton_toggled_cb),
642           self);
643     }
644   else if (GTK_IS_COMBO_BOX (widget))
645     {
646       /* The combo box's model has to contain the param value in its first
647        * column (as a string) */
648       const gchar *str;
649       GtkTreeModel *model;
650       GtkTreeIter iter;
651       gboolean valid;
652
653       str = empathy_account_settings_get_string (priv->settings, param_name);
654       model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
655
656       valid = gtk_tree_model_get_iter_first (model, &iter);
657       while (valid)
658         {
659           gchar *name;
660
661           gtk_tree_model_get (model, &iter, 0, &name, -1);
662           if (!tp_strdiff (name, str))
663             {
664               gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter);
665               valid = FALSE;
666             }
667           else
668             {
669               valid = gtk_tree_model_iter_next (model, &iter);
670             }
671
672           g_free (name);
673         }
674
675       g_signal_connect (widget, "changed",
676           G_CALLBACK (account_widget_combobox_changed_cb),
677           self);
678     }
679   else
680     {
681       DEBUG ("Unknown type of widget for param %s", param_name);
682     }
683
684   gtk_widget_set_sensitive (widget,
685       empathy_account_settings_param_is_supported (priv->settings, param_name));
686 }
687
688 static GHashTable *
689 build_translated_params (void)
690 {
691   GHashTable *hash;
692
693   hash = g_hash_table_new (g_str_hash, g_str_equal);
694   g_hash_table_insert (hash, "account", _("Account"));
695   g_hash_table_insert (hash, "password", _("Password"));
696   g_hash_table_insert (hash, "server", _("Server"));
697   g_hash_table_insert (hash, "port", _("Port"));
698
699   return hash;
700 }
701
702 static gchar *
703 account_widget_generic_format_param_name (const gchar *param_name)
704 {
705   gchar *str;
706   gchar *p;
707   static GHashTable *translated_params = NULL;
708
709   if (G_UNLIKELY (translated_params == NULL))
710     translated_params = build_translated_params ();
711
712   /* Translate most common parameters */
713   str = g_hash_table_lookup (translated_params, param_name);
714   if (str != NULL)
715     return g_strdup (str);
716
717   str = g_strdup (param_name);
718
719   if (str && g_ascii_isalpha (str[0]))
720     str[0] = g_ascii_toupper (str[0]);
721
722   while ((p = strchr (str, '-')) != NULL)
723     {
724       if (p[1] != '\0' && g_ascii_isalpha (p[1]))
725         {
726           p[0] = ' ';
727           p[1] = g_ascii_toupper (p[1]);
728         }
729
730       p++;
731     }
732
733   return str;
734 }
735
736 static void
737 accounts_widget_generic_setup (EmpathyAccountWidget *self,
738     GtkWidget *table_common_settings,
739     GtkWidget *table_advanced_settings)
740 {
741   TpConnectionManagerParam *params, *param;
742   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
743
744   params = empathy_account_settings_get_tp_params (priv->settings);
745
746   for (param = params; param != NULL && param->name != NULL; param++)
747     {
748       GtkWidget       *table_settings;
749       guint            n_rows = 0;
750       GtkWidget       *widget = NULL;
751       gchar           *param_name_formatted;
752
753       if (param->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED)
754         table_settings = table_common_settings;
755       else if (priv->simple)
756         return;
757       else
758         table_settings = table_advanced_settings;
759
760       param_name_formatted = account_widget_generic_format_param_name
761         (param->name);
762       g_object_get (table_settings, "n-rows", &n_rows, NULL);
763       gtk_table_resize (GTK_TABLE (table_settings), ++n_rows, 2);
764
765       if (param->dbus_signature[0] == 's')
766         {
767           gchar *str;
768
769           str = g_strdup_printf (_("%s:"), param_name_formatted);
770           widget = gtk_label_new (str);
771           gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
772           g_free (str);
773
774           gtk_table_attach (GTK_TABLE (table_settings),
775               widget,
776               0, 1,
777               n_rows - 1, n_rows,
778               GTK_FILL, 0,
779               0, 0);
780           gtk_widget_show (widget);
781
782           widget = gtk_entry_new ();
783           if (strcmp (param->name, "account") == 0)
784             {
785               g_signal_connect (widget, "realize",
786                   G_CALLBACK (gtk_widget_grab_focus),
787                   NULL);
788             }
789           gtk_table_attach (GTK_TABLE (table_settings),
790               widget,
791               1, 2,
792               n_rows - 1, n_rows,
793               GTK_FILL | GTK_EXPAND, 0,
794               0, 0);
795           gtk_widget_show (widget);
796         }
797       /* int types: ynqiuxt. double type is 'd' */
798       else if (param->dbus_signature[0] == 'y' ||
799           param->dbus_signature[0] == 'n' ||
800           param->dbus_signature[0] == 'q' ||
801           param->dbus_signature[0] == 'i' ||
802           param->dbus_signature[0] == 'u' ||
803           param->dbus_signature[0] == 'x' ||
804           param->dbus_signature[0] == 't' ||
805           param->dbus_signature[0] == 'd')
806         {
807           gchar   *str = NULL;
808           gdouble  minint = 0;
809           gdouble  maxint = 0;
810           gdouble  step = 1;
811
812           switch (param->dbus_signature[0])
813             {
814             case 'y': minint = G_MININT8;  maxint = G_MAXINT8;   break;
815             case 'n': minint = G_MININT16; maxint = G_MAXINT16;  break;
816             case 'q': minint = 0;          maxint = G_MAXUINT16; break;
817             case 'i': minint = G_MININT32; maxint = G_MAXINT32;  break;
818             case 'u': minint = 0;          maxint = G_MAXUINT32; break;
819             case 'x': minint = G_MININT64; maxint = G_MAXINT64;  break;
820             case 't': minint = 0;          maxint = G_MAXUINT64; break;
821             case 'd': minint = G_MININT32; maxint = G_MAXINT32;
822               step = 0.1; break;
823             default: g_assert_not_reached ();
824             }
825
826           str = g_strdup_printf (_("%s:"), param_name_formatted);
827           widget = gtk_label_new (str);
828           gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
829           g_free (str);
830
831           gtk_table_attach (GTK_TABLE (table_settings),
832               widget,
833               0, 1,
834               n_rows - 1, n_rows,
835               GTK_FILL, 0,
836               0, 0);
837           gtk_widget_show (widget);
838
839           widget = gtk_spin_button_new_with_range (minint, maxint, step);
840           gtk_table_attach (GTK_TABLE (table_settings),
841               widget,
842               1, 2,
843               n_rows - 1, n_rows,
844               GTK_FILL | GTK_EXPAND, 0,
845               0, 0);
846           gtk_widget_show (widget);
847         }
848       else if (param->dbus_signature[0] == 'b')
849         {
850           widget = gtk_check_button_new_with_label (param_name_formatted);
851           gtk_table_attach (GTK_TABLE (table_settings),
852               widget,
853               0, 2,
854               n_rows - 1, n_rows,
855               GTK_FILL | GTK_EXPAND, 0,
856               0, 0);
857           gtk_widget_show (widget);
858         }
859       else
860         {
861           DEBUG ("Unknown signature for param %s: %s",
862               param_name_formatted, param->dbus_signature);
863         }
864
865       if (widget)
866         empathy_account_widget_setup_widget (self, widget, param->name);
867
868       g_free (param_name_formatted);
869     }
870 }
871
872 static void
873 account_widget_handle_params_valist (EmpathyAccountWidget *self,
874     const gchar *first_widget,
875     va_list args)
876 {
877   GObject *object;
878   const gchar *name;
879
880   for (name = first_widget; name; name = va_arg (args, const gchar *))
881     {
882       const gchar *param_name;
883
884       param_name = va_arg (args, const gchar *);
885       object = gtk_builder_get_object (self->ui_details->gui, name);
886
887       if (!object)
888         {
889           g_warning ("Builder is missing object '%s'.", name);
890           continue;
891         }
892
893       empathy_account_widget_setup_widget (self, GTK_WIDGET (object),
894           param_name);
895     }
896 }
897
898 static void
899 account_widget_cancel_clicked_cb (GtkWidget *button,
900     EmpathyAccountWidget *self)
901 {
902   g_signal_emit (self, signals[CANCELLED], 0);
903 }
904
905 static void
906 account_widget_account_enabled_cb (GObject *source_object,
907     GAsyncResult *res,
908     gpointer user_data)
909 {
910   GError *error = NULL;
911   TpAccount *account = TP_ACCOUNT (source_object);
912   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
913   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
914
915   tp_account_set_enabled_finish (account, res, &error);
916
917   if (error != NULL)
918     {
919       DEBUG ("Could not enable the account: %s", error->message);
920       g_error_free (error);
921     }
922   else
923     {
924       empathy_connect_new_account (account, priv->account_manager);
925     }
926
927   /* unref widget - part of the workaround */
928   g_object_unref (widget);
929 }
930
931 static void
932 account_widget_applied_cb (GObject *source_object,
933     GAsyncResult *res,
934     gpointer user_data)
935 {
936   GError *error = NULL;
937   TpAccount *account;
938   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (source_object);
939   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
940   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
941   gboolean reconnect_required;
942
943   empathy_account_settings_apply_finish (settings, res, &reconnect_required,
944       &error);
945
946   if (error != NULL)
947     {
948       DEBUG ("Could not apply changes to account: %s", error->message);
949       g_error_free (error);
950       return;
951     }
952
953   account = empathy_account_settings_get_account (priv->settings);
954
955   if (account != NULL)
956     {
957       if (priv->creating_account)
958         {
959           /* By default, when an account is created, we enable it. */
960
961           /* workaround to keep widget alive during async call */
962           g_object_ref (widget);
963
964           tp_account_set_enabled_async (account, TRUE,
965               account_widget_account_enabled_cb, widget);
966           g_signal_emit (widget, signals[ACCOUNT_CREATED], 0, account);
967         }
968       else
969         {
970           /* If the account was offline, we always want to try reconnecting,
971            * to give it a chance to connect if the previous params were wrong.
972            * tp_account_reconnect_async() won't do anything if the requested
973            * presence is offline anyway. */
974           if (tp_account_get_connection_status (account, NULL) ==
975               TP_CONNECTION_STATUS_DISCONNECTED)
976             reconnect_required = TRUE;
977
978           if (reconnect_required && tp_account_is_enabled (account)
979               && tp_account_is_enabled (account))
980             {
981               /* After having applied changes to a user account, we
982                * reconnect it if needed. This is done so the new
983                * information entered by the user is validated on the server. */
984               tp_account_reconnect_async (account, NULL, NULL);
985             }
986         }
987     }
988
989   if (!priv->destroyed)
990     account_widget_set_control_buttons_sensitivity (widget, FALSE);
991
992   priv->contains_pending_changes = FALSE;
993
994   /* unref the widget - part of the workaround */
995   g_object_unref (widget);
996 }
997
998 static void
999 account_widget_apply_and_log_in (EmpathyAccountWidget *self)
1000 {
1001   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1002   gboolean display_name_overridden;
1003
1004   if (priv->radiobutton_reuse != NULL)
1005     {
1006       gboolean reuse = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
1007             priv->radiobutton_reuse));
1008
1009       DEBUG ("Set register param: %d", !reuse);
1010       empathy_account_settings_set_boolean (priv->settings, "register", !reuse);
1011     }
1012
1013   g_object_get (priv->settings,
1014       "display-name-overridden", &display_name_overridden, NULL);
1015
1016   if (priv->creating_account || !display_name_overridden)
1017     {
1018       gchar *display_name;
1019
1020       /* set default display name for new accounts or update if user didn't
1021        * manually override it. */
1022       display_name = empathy_account_widget_get_default_display_name (self);
1023
1024       empathy_account_settings_set_display_name_async (priv->settings,
1025           display_name, NULL, NULL);
1026
1027       g_free (display_name);
1028     }
1029
1030   /* workaround to keep widget alive during async call */
1031   g_object_ref (self);
1032   empathy_account_settings_apply_async (priv->settings,
1033       account_widget_applied_cb, self);
1034 }
1035
1036 static void
1037 account_widget_apply_clicked_cb (GtkWidget *button,
1038     EmpathyAccountWidget *self)
1039 {
1040     account_widget_apply_and_log_in (self);
1041 }
1042
1043 static void
1044 account_widget_setup_generic (EmpathyAccountWidget *self)
1045 {
1046   GtkWidget *table_common_settings;
1047   GtkWidget *table_advanced_settings;
1048
1049   table_common_settings = GTK_WIDGET (gtk_builder_get_object
1050       (self->ui_details->gui, "table_common_settings"));
1051   table_advanced_settings = GTK_WIDGET (gtk_builder_get_object
1052       (self->ui_details->gui, "table_advanced_settings"));
1053
1054   accounts_widget_generic_setup (self, table_common_settings,
1055       table_advanced_settings);
1056
1057   g_object_unref (self->ui_details->gui);
1058 }
1059
1060 static void
1061 account_widget_settings_ready_cb (EmpathyAccountSettings *settings,
1062     GParamSpec *pspec,
1063     gpointer user_data)
1064 {
1065   EmpathyAccountWidget *self = user_data;
1066   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1067
1068   if (empathy_account_settings_is_ready (priv->settings))
1069     account_widget_setup_generic (self);
1070 }
1071
1072 static void
1073 account_widget_build_generic (EmpathyAccountWidget *self,
1074     const char *filename)
1075 {
1076   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1077   GtkWidget *expander_advanced;
1078
1079   self->ui_details->gui = empathy_builder_get_file (filename,
1080       "table_common_settings", &priv->table_common_settings,
1081       "vbox_generic_settings", &self->ui_details->widget,
1082       "expander_advanced_settings", &expander_advanced,
1083       NULL);
1084
1085   if (priv->simple)
1086     gtk_widget_hide (expander_advanced);
1087
1088   g_object_ref (self->ui_details->gui);
1089
1090   if (empathy_account_settings_is_ready (priv->settings))
1091     account_widget_setup_generic (self);
1092   else
1093     g_signal_connect (priv->settings, "notify::ready",
1094         G_CALLBACK (account_widget_settings_ready_cb), self);
1095 }
1096
1097 static void
1098 account_widget_launch_external_clicked (GtkWidget *button,
1099     TpAccount *account)
1100 {
1101   if (!tp_strdiff (tp_account_get_storage_provider (account),
1102         "com.meego.libsocialweb"))
1103     {
1104       /* we know how to handle this external provider */
1105       GDesktopAppInfo *desktop_info;
1106       GError *error = NULL;
1107       GdkAppLaunchContext *context = NULL;
1108       GdkDisplay *display;
1109       gchar *cmd;
1110       GAppInfo *app_info;
1111
1112       desktop_info = g_desktop_app_info_new ("gnome-control-center.desktop");
1113       if (desktop_info == NULL)
1114         {
1115           g_critical ("Could not locate 'gnome-control-center.desktop'");
1116           return;
1117         }
1118
1119       /* glib doesn't have API to start a desktop file with args... (#637875) */
1120       cmd = g_strdup_printf ("%s bisho.desktop", g_app_info_get_commandline (
1121             (GAppInfo *) desktop_info));
1122
1123       app_info = g_app_info_create_from_commandline (cmd, NULL, 0, &error);
1124       g_free (cmd);
1125
1126       if (app_info == NULL)
1127         {
1128           DEBUG ("Failed to create app info: %s", error->message);
1129           g_error_free (error);
1130           goto out;
1131         }
1132
1133       display = gdk_display_get_default ();
1134       context = gdk_display_get_app_launch_context (display);
1135
1136       if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
1137             &error))
1138         {
1139           g_critical ("Failed to bisho: %s", error->message);
1140           g_clear_error (&error);
1141         }
1142
1143 out:
1144       g_object_unref (desktop_info);
1145       tp_clear_object (&app_info);
1146       tp_clear_object (&context);
1147     }
1148 }
1149
1150 static void
1151 account_widget_build_external (EmpathyAccountWidget *self,
1152     EmpathyAccountSettings *settings)
1153 {
1154   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1155   TpAccount *account = empathy_account_settings_get_account (settings);
1156   GtkWidget *bar, *widget;
1157   gchar *str;
1158
1159   self->ui_details->widget = gtk_vbox_new (FALSE, 6);
1160   priv->table_common_settings = gtk_table_new (1, 2, FALSE);
1161
1162   if (!tp_strdiff (tp_account_get_storage_provider (account),
1163         "com.meego.libsocialweb"))
1164     {
1165       /* we know how to handle this external provider */
1166       str = g_strdup_printf (
1167           _("The account %s is edited via My Web Accounts."),
1168           empathy_account_settings_get_display_name (settings));
1169     }
1170   else
1171     {
1172       str = g_strdup_printf (
1173           _("The account %s cannot be edited in Empathy."),
1174           empathy_account_settings_get_display_name (settings));
1175     }
1176
1177   widget = gtk_label_new (str);
1178   gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
1179   g_free (str);
1180
1181   bar = gtk_info_bar_new ();
1182   gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_INFO);
1183   gtk_container_add (
1184       GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))),
1185       widget);
1186   gtk_container_set_border_width (GTK_CONTAINER (bar), 6);
1187
1188   if (!tp_strdiff (tp_account_get_storage_provider (account),
1189         "com.meego.libsocialweb"))
1190     {
1191       /* we know how to handle this external provider */
1192       widget = gtk_info_bar_add_button (GTK_INFO_BAR (bar),
1193           _("Launch My Web Accounts"), RESPONSE_LAUNCH);
1194
1195       g_signal_connect (widget, "clicked",
1196           G_CALLBACK (account_widget_launch_external_clicked), account);
1197     }
1198
1199   gtk_box_pack_start (GTK_BOX (self->ui_details->widget), bar,
1200       FALSE, TRUE, 0);
1201   gtk_box_pack_start (GTK_BOX (self->ui_details->widget),
1202       priv->table_common_settings, FALSE, TRUE, 0);
1203
1204   gtk_widget_show_all (self->ui_details->widget);
1205 }
1206
1207 static void
1208 account_widget_build_salut (EmpathyAccountWidget *self,
1209     const char *filename)
1210 {
1211   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1212   GtkWidget *expander_advanced;
1213
1214   self->ui_details->gui = empathy_builder_get_file (filename,
1215       "table_common_settings", &priv->table_common_settings,
1216       "vbox_salut_settings", &self->ui_details->widget,
1217       "expander_advanced_settings", &expander_advanced,
1218       NULL);
1219
1220   empathy_account_widget_handle_params (self,
1221       "entry_published", "published-name",
1222       "entry_nickname", "nickname",
1223       "entry_first_name", "first-name",
1224       "entry_last_name", "last-name",
1225       "entry_email", "email",
1226       "entry_jid", "jid",
1227       NULL);
1228
1229   if (priv->simple)
1230     gtk_widget_hide (expander_advanced);
1231
1232   self->ui_details->default_focus = g_strdup ("entry_first_name");
1233 }
1234
1235 static void
1236 account_widget_build_irc (EmpathyAccountWidget *self,
1237   const char *filename)
1238 {
1239   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1240
1241   empathy_account_settings_set_regex (priv->settings, "account",
1242       ACCOUNT_REGEX_IRC);
1243   empathy_account_settings_set_regex (priv->settings, "username",
1244       USERNAME_REGEX_IRC);
1245
1246   if (priv->simple)
1247     {
1248       priv->irc_network_chooser = empathy_account_widget_irc_build_simple (self,
1249           filename);
1250     }
1251   else
1252     {
1253       priv->irc_network_chooser = empathy_account_widget_irc_build (self,
1254           filename, &priv->table_common_settings);
1255     }
1256 }
1257
1258 static void
1259 account_widget_build_sip (EmpathyAccountWidget *self,
1260   const char *filename)
1261 {
1262   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1263   empathy_account_widget_sip_build (self, filename,
1264     &priv->table_common_settings);
1265
1266   if (priv->simple)
1267     {
1268       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1269               self->ui_details->gui, "remember_password_simple"));
1270     }
1271   else
1272     {
1273       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1274               self->ui_details->gui, "remember_password"));
1275     }
1276 }
1277
1278 static void
1279 account_widget_build_msn (EmpathyAccountWidget *self,
1280     const char *filename)
1281 {
1282   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1283
1284   empathy_account_settings_set_regex (priv->settings, "account",
1285       ACCOUNT_REGEX_MSN);
1286
1287   if (priv->simple)
1288     {
1289       self->ui_details->gui = empathy_builder_get_file (filename,
1290           "vbox_msn_simple", &self->ui_details->widget,
1291           NULL);
1292
1293       empathy_account_widget_handle_params (self,
1294           "entry_id_simple", "account",
1295           "entry_password_simple", "password",
1296           NULL);
1297
1298       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1299
1300       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1301               self->ui_details->gui, "remember_password_simple"));
1302     }
1303   else
1304     {
1305       self->ui_details->gui = empathy_builder_get_file (filename,
1306           "table_common_msn_settings", &priv->table_common_settings,
1307           "vbox_msn_settings", &self->ui_details->widget,
1308           NULL);
1309
1310       empathy_account_widget_handle_params (self,
1311           "entry_id", "account",
1312           "entry_password", "password",
1313           "entry_server", "server",
1314           "spinbutton_port", "port",
1315           NULL);
1316
1317       self->ui_details->default_focus = g_strdup ("entry_id");
1318
1319       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1320               self->ui_details->gui, "remember_password"));
1321     }
1322 }
1323
1324 static void
1325 suffix_id_widget_changed_cb (GtkWidget *entry,
1326     EmpathyAccountWidget *self)
1327 {
1328   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1329   const gchar *account;
1330
1331   g_assert (priv->jid_suffix != NULL);
1332
1333   account_widget_entry_changed_common (self, GTK_ENTRY (entry), FALSE);
1334
1335   account = empathy_account_settings_get_string (priv->settings, "account");
1336   if (!EMP_STR_EMPTY (account) &&
1337       !g_str_has_suffix (account, priv->jid_suffix))
1338     {
1339       gchar *tmp;
1340
1341       tmp = g_strdup_printf ("%s%s", account, priv->jid_suffix);
1342
1343       DEBUG ("Change account from '%s' to '%s'", account, tmp);
1344
1345       empathy_account_settings_set_string (priv->settings, "account", tmp);
1346       g_free (tmp);
1347     }
1348
1349   empathy_account_widget_changed (self);
1350 }
1351
1352 static gchar *
1353 remove_jid_suffix (EmpathyAccountWidget *self,
1354     const gchar *str)
1355 {
1356   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1357
1358   g_assert (priv->jid_suffix != NULL);
1359
1360   if (!g_str_has_suffix (str, priv->jid_suffix))
1361     return g_strdup (str);
1362
1363   return g_strndup (str, strlen (str) - strlen (priv->jid_suffix));
1364 }
1365
1366 static void
1367 setup_id_widget_with_suffix (EmpathyAccountWidget *self,
1368     GtkWidget *widget,
1369     const gchar *suffix)
1370 {
1371   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1372   const gchar *str = NULL;
1373
1374   g_object_set_data_full (G_OBJECT (widget), "param_name",
1375       g_strdup ("account"), g_free);
1376
1377   g_assert (priv->jid_suffix == NULL);
1378   priv->jid_suffix = g_strdup (suffix);
1379
1380   str = empathy_account_settings_get_string (priv->settings, "account");
1381   if (str != NULL)
1382     {
1383       gchar *tmp;
1384
1385       tmp = remove_jid_suffix (self, str);
1386       gtk_entry_set_text (GTK_ENTRY (widget), tmp);
1387       g_free (tmp);
1388     }
1389
1390   priv->param_account_widget = widget;
1391
1392   g_signal_connect (widget, "changed",
1393       G_CALLBACK (suffix_id_widget_changed_cb), self);
1394 }
1395
1396 static Service
1397 account_widget_get_service (EmpathyAccountWidget *self)
1398 {
1399   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1400   const gchar *icon_name, *service;
1401
1402   icon_name = empathy_account_settings_get_icon_name (priv->settings);
1403   service = empathy_account_settings_get_service (priv->settings);
1404
1405   /* Previous versions of Empathy didn't set the Service property on Facebook
1406    * and gtalk accounts, so we check using the icon name as well. */
1407   if (!tp_strdiff (icon_name, "im-google-talk") ||
1408       !tp_strdiff (service, "google-talk"))
1409     return GTALK_SERVICE;
1410
1411   if (!tp_strdiff (icon_name, "im-facebook") ||
1412       !tp_strdiff (service, "facebook"))
1413     return FACEBOOK_SERVICE;
1414
1415   return NO_SERVICE;
1416 }
1417
1418 static void
1419 account_widget_build_jabber (EmpathyAccountWidget *self,
1420     const char *filename)
1421 {
1422   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1423   GtkWidget *spinbutton_port;
1424   GtkWidget *checkbutton_ssl;
1425   GtkWidget *label_id, *label_password;
1426   GtkWidget *label_id_create, *label_password_create;
1427   GtkWidget *label_example_fb;
1428   GtkWidget *label_example;
1429   GtkWidget *expander_advanced;
1430   GtkWidget *entry_id;
1431   Service service;
1432
1433   service = account_widget_get_service (self);
1434
1435   empathy_account_settings_set_regex (priv->settings, "account",
1436       ACCOUNT_REGEX_JABBER);
1437
1438   if (priv->simple && service == NO_SERVICE)
1439     {
1440       /* Simple widget for XMPP */
1441       self->ui_details->gui = empathy_builder_get_file (filename,
1442           "vbox_jabber_simple", &self->ui_details->widget,
1443           "label_id_simple", &label_id,
1444           "label_id_create", &label_id_create,
1445           "label_password_simple", &label_password,
1446           "label_password_create", &label_password_create,
1447           NULL);
1448
1449       if (empathy_account_settings_get_boolean (priv->settings, "register"))
1450         {
1451           gtk_widget_hide (label_id);
1452           gtk_widget_hide (label_password);
1453           gtk_widget_show (label_id_create);
1454           gtk_widget_show (label_password_create);
1455         }
1456
1457       empathy_account_widget_handle_params (self,
1458           "entry_id_simple", "account",
1459           "entry_password_simple", "password",
1460           NULL);
1461
1462       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1463
1464       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1465               self->ui_details->gui, "remember_password_simple"));
1466     }
1467   else if (priv->simple && service == GTALK_SERVICE)
1468     {
1469       /* Simple widget for Google Talk */
1470       self->ui_details->gui = empathy_builder_get_file (filename,
1471           "vbox_gtalk_simple", &self->ui_details->widget,
1472           NULL);
1473
1474       empathy_account_widget_handle_params (self,
1475           "entry_id_g_simple", "account",
1476           "entry_password_g_simple", "password",
1477           NULL);
1478
1479       self->ui_details->default_focus = g_strdup ("entry_id_g_simple");
1480
1481       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1482               self->ui_details->gui, "remember_password_g_simple"));
1483     }
1484   else if (priv->simple && service == FACEBOOK_SERVICE)
1485     {
1486       /* Simple widget for Facebook */
1487       self->ui_details->gui = empathy_builder_get_file (filename,
1488           "vbox_fb_simple", &self->ui_details->widget,
1489           "entry_id_fb_simple", &entry_id,
1490           NULL);
1491
1492       empathy_account_widget_handle_params (self,
1493           "entry_password_fb_simple", "password",
1494           NULL);
1495
1496       setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
1497
1498       self->ui_details->default_focus = g_strdup ("entry_id_fb_simple");
1499
1500       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1501               self->ui_details->gui, "remember_password_fb_simple"));
1502     }
1503   else
1504     {
1505       ServiceInfo info = services_infos[service];
1506
1507       /* Full widget for XMPP, Google Talk and Facebook*/
1508       self->ui_details->gui = empathy_builder_get_file (filename,
1509           "table_common_settings", &priv->table_common_settings,
1510           "vbox_jabber_settings", &self->ui_details->widget,
1511           "spinbutton_port", &spinbutton_port,
1512           "checkbutton_ssl", &checkbutton_ssl,
1513           "label_username_f_example", &label_example_fb,
1514           info.label_username_example, &label_example,
1515           "expander_advanced", &expander_advanced,
1516           "entry_id", &entry_id,
1517           "label_id", &label_id,
1518           NULL);
1519
1520       empathy_account_widget_handle_params (self,
1521           "entry_password", "password",
1522           "entry_resource", "resource",
1523           "entry_server", "server",
1524           "spinbutton_port", "port",
1525           "spinbutton_priority", "priority",
1526           "checkbutton_ssl", "old-ssl",
1527           "checkbutton_ignore_ssl_errors", "ignore-ssl-errors",
1528           "checkbutton_encryption", "require-encryption",
1529           NULL);
1530
1531       if (service == FACEBOOK_SERVICE)
1532         {
1533           gtk_label_set_label (GTK_LABEL (label_id), _("Username:"));
1534
1535           /* Facebook special case the entry ID widget to hide the
1536            * "@chat.facebook.com" part */
1537           setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
1538         }
1539       else
1540         {
1541           empathy_account_widget_setup_widget (self, entry_id, "account");
1542         }
1543
1544       self->ui_details->default_focus = g_strdup ("entry_id");
1545       priv->spinbutton_port = spinbutton_port;
1546
1547       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1548               self->ui_details->gui, "remember_password"));
1549
1550       g_signal_connect (checkbutton_ssl, "toggled",
1551           G_CALLBACK (account_widget_jabber_ssl_toggled_cb),
1552           self);
1553
1554       if (service == FACEBOOK_SERVICE)
1555         {
1556           GtkContainer *parent;
1557           GList *children;
1558
1559           /* Removing the label from list of focusable widgets */
1560           parent = GTK_CONTAINER (gtk_widget_get_parent (label_example_fb));
1561           children = gtk_container_get_children (parent);
1562           children = g_list_remove (children, label_example_fb);
1563           gtk_container_set_focus_chain (parent, children);
1564           g_list_free (children);
1565         }
1566
1567       gtk_widget_show (label_example);
1568
1569       if (!info.show_advanced)
1570         gtk_widget_hide (expander_advanced);
1571     }
1572 }
1573
1574 static void
1575 account_widget_build_icq (EmpathyAccountWidget *self,
1576     const char *filename)
1577 {
1578   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1579   GtkWidget *spinbutton_port;
1580
1581   empathy_account_settings_set_regex (priv->settings, "account",
1582       ACCOUNT_REGEX_ICQ);
1583
1584   if (priv->simple)
1585     {
1586       self->ui_details->gui = empathy_builder_get_file (filename,
1587           "vbox_icq_simple", &self->ui_details->widget,
1588           NULL);
1589
1590       empathy_account_widget_handle_params (self,
1591           "entry_uin_simple", "account",
1592           "entry_password_simple", "password",
1593           NULL);
1594
1595       self->ui_details->default_focus = g_strdup ("entry_uin_simple");
1596
1597       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1598               self->ui_details->gui, "remember_password_simple"));
1599     }
1600   else
1601     {
1602       self->ui_details->gui = empathy_builder_get_file (filename,
1603           "table_common_settings", &priv->table_common_settings,
1604           "vbox_icq_settings", &self->ui_details->widget,
1605           "spinbutton_port", &spinbutton_port,
1606           NULL);
1607
1608       empathy_account_widget_handle_params (self,
1609           "entry_uin", "account",
1610           "entry_password", "password",
1611           "entry_server", "server",
1612           "spinbutton_port", "port",
1613           "entry_charset", "charset",
1614           NULL);
1615
1616       self->ui_details->default_focus = g_strdup ("entry_uin");
1617
1618       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1619               self->ui_details->gui, "remember_password"));
1620     }
1621 }
1622
1623 static void
1624 account_widget_build_aim (EmpathyAccountWidget *self,
1625     const char *filename)
1626 {
1627   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1628   GtkWidget *spinbutton_port;
1629
1630   if (priv->simple)
1631     {
1632       self->ui_details->gui = empathy_builder_get_file (filename,
1633           "vbox_aim_simple", &self->ui_details->widget,
1634           NULL);
1635
1636       empathy_account_widget_handle_params (self,
1637           "entry_screenname_simple", "account",
1638           "entry_password_simple", "password",
1639           NULL);
1640
1641       self->ui_details->default_focus = g_strdup ("entry_screenname_simple");
1642
1643       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1644               self->ui_details->gui, "remember_password_simple"));
1645     }
1646   else
1647     {
1648       self->ui_details->gui = empathy_builder_get_file (filename,
1649           "table_common_settings", &priv->table_common_settings,
1650           "vbox_aim_settings", &self->ui_details->widget,
1651           "spinbutton_port", &spinbutton_port,
1652           NULL);
1653
1654       empathy_account_widget_handle_params (self,
1655           "entry_screenname", "account",
1656           "entry_password", "password",
1657           "entry_server", "server",
1658           "spinbutton_port", "port",
1659           NULL);
1660
1661       self->ui_details->default_focus = g_strdup ("entry_screenname");
1662
1663       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1664               self->ui_details->gui, "remember_password"));
1665     }
1666 }
1667
1668 static void
1669 account_widget_build_yahoo (EmpathyAccountWidget *self,
1670     const char *filename)
1671 {
1672   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1673
1674   empathy_account_settings_set_regex (priv->settings, "account",
1675       ACCOUNT_REGEX_YAHOO);
1676
1677   if (priv->simple)
1678     {
1679       self->ui_details->gui = empathy_builder_get_file (filename,
1680           "vbox_yahoo_simple", &self->ui_details->widget,
1681           NULL);
1682
1683       empathy_account_widget_handle_params (self,
1684           "entry_id_simple", "account",
1685           "entry_password_simple", "password",
1686           NULL);
1687
1688       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1689
1690       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1691               self->ui_details->gui, "remember_password_simple"));
1692     }
1693   else
1694     {
1695       self->ui_details->gui = empathy_builder_get_file (filename,
1696           "table_common_settings", &priv->table_common_settings,
1697           "vbox_yahoo_settings", &self->ui_details->widget,
1698           NULL);
1699
1700       empathy_account_widget_handle_params (self,
1701           "entry_id", "account",
1702           "entry_password", "password",
1703           "entry_locale", "room-list-locale",
1704           "entry_charset", "charset",
1705           "spinbutton_port", "port",
1706           "checkbutton_ignore_invites", "ignore-invites",
1707           NULL);
1708
1709       self->ui_details->default_focus = g_strdup ("entry_id");
1710
1711       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1712               self->ui_details->gui, "remember_password"));
1713     }
1714 }
1715
1716 static void
1717 account_widget_build_groupwise (EmpathyAccountWidget *self,
1718     const char *filename)
1719 {
1720   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1721
1722   if (priv->simple)
1723     {
1724       self->ui_details->gui = empathy_builder_get_file (filename,
1725           "vbox_groupwise_simple", &self->ui_details->widget,
1726           NULL);
1727
1728       empathy_account_widget_handle_params (self,
1729           "entry_id_simple", "account",
1730           "entry_password_simple", "password",
1731           NULL);
1732
1733       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1734
1735       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1736               self->ui_details->gui, "remember_password_simple"));
1737     }
1738   else
1739     {
1740       self->ui_details->gui = empathy_builder_get_file (filename,
1741           "table_common_groupwise_settings", &priv->table_common_settings,
1742           "vbox_groupwise_settings", &self->ui_details->widget,
1743           NULL);
1744
1745       empathy_account_widget_handle_params (self,
1746           "entry_id", "account",
1747           "entry_password", "password",
1748           "entry_server", "server",
1749           "spinbutton_port", "port",
1750           NULL);
1751
1752       self->ui_details->default_focus = g_strdup ("entry_id");
1753
1754       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1755               self->ui_details->gui, "remember_password"));
1756     }
1757 }
1758
1759 static void
1760 account_widget_destroy_cb (GtkWidget *widget,
1761     EmpathyAccountWidget *self)
1762 {
1763   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1764   /* set the destroyed flag - workaround */
1765   priv->destroyed = TRUE;
1766
1767   g_object_unref (self);
1768 }
1769
1770 void
1771 empathy_account_widget_set_other_accounts_exist (EmpathyAccountWidget *self,
1772     gboolean others_exist)
1773 {
1774   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1775
1776   priv->other_accounts_exist = others_exist;
1777
1778   if (priv->creating_account)
1779     account_widget_handle_control_buttons_sensitivity (self);
1780 }
1781
1782 static void
1783 do_set_property (GObject *object,
1784     guint prop_id,
1785     const GValue *value,
1786     GParamSpec *pspec)
1787 {
1788   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1789
1790   switch (prop_id)
1791     {
1792     case PROP_SETTINGS:
1793       priv->settings = g_value_dup_object (value);
1794       break;
1795     case PROP_SIMPLE:
1796       priv->simple = g_value_get_boolean (value);
1797       break;
1798     case PROP_CREATING_ACCOUNT:
1799       priv->creating_account = g_value_get_boolean (value);
1800       break;
1801     case PROP_OTHER_ACCOUNTS_EXIST:
1802       empathy_account_widget_set_other_accounts_exist (
1803           EMPATHY_ACCOUNT_WIDGET (object), g_value_get_boolean (value));
1804       break;
1805     default:
1806       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1807     }
1808 }
1809
1810 static void
1811 do_get_property (GObject *object,
1812     guint prop_id,
1813     GValue *value,
1814     GParamSpec *pspec)
1815 {
1816   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1817
1818   switch (prop_id)
1819     {
1820     case PROP_PROTOCOL:
1821       g_value_set_string (value,
1822         empathy_account_settings_get_protocol (priv->settings));
1823       break;
1824     case PROP_SETTINGS:
1825       g_value_set_object (value, priv->settings);
1826       break;
1827     case PROP_SIMPLE:
1828       g_value_set_boolean (value, priv->simple);
1829       break;
1830     case PROP_CREATING_ACCOUNT:
1831       g_value_set_boolean (value, priv->creating_account);
1832       break;
1833     case PROP_OTHER_ACCOUNTS_EXIST:
1834       g_value_set_boolean (value, priv->other_accounts_exist);
1835       break;
1836     default:
1837       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1838     }
1839 }
1840
1841 static void
1842 set_apply_button (EmpathyAccountWidget *self)
1843 {
1844   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1845   GtkWidget *image;
1846
1847   /* We can't use the stock button as its accelerator ('A') clashes with the
1848    * Add button. */
1849   gtk_button_set_use_stock (GTK_BUTTON (priv->apply_button), FALSE);
1850
1851   gtk_button_set_label (GTK_BUTTON (priv->apply_button), _("A_pply"));
1852   gtk_button_set_use_underline (GTK_BUTTON (priv->apply_button), TRUE);
1853
1854   image = gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
1855   gtk_button_set_image (GTK_BUTTON (priv->apply_button), image);
1856 }
1857
1858 static void
1859 presence_changed_cb (TpAccountManager *manager,
1860     TpConnectionPresenceType state,
1861     const gchar *status,
1862     const gchar *message,
1863     EmpathyAccountWidget *self)
1864 {
1865   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1866
1867   if (priv->destroyed)
1868     return;
1869
1870   if (priv->apply_button == NULL)
1871     /* This button doesn't exist in 'simple' mode */
1872     return;
1873
1874   if (state > TP_CONNECTION_PRESENCE_TYPE_OFFLINE &&
1875       priv->creating_account)
1876     {
1877       /* We are online and creating a new account, display a Login button */
1878       GtkWidget *image;
1879
1880       gtk_button_set_use_stock (GTK_BUTTON (priv->apply_button), FALSE);
1881       gtk_button_set_label (GTK_BUTTON (priv->apply_button), _("L_og in"));
1882
1883       image = gtk_image_new_from_stock (GTK_STOCK_CONNECT,
1884           GTK_ICON_SIZE_BUTTON);
1885       gtk_button_set_image (GTK_BUTTON (priv->apply_button), image);
1886     }
1887   else
1888     {
1889       /* We are offline or modifying an existing account, display
1890        * a Save button */
1891       set_apply_button (self);
1892     }
1893 }
1894
1895 static void
1896 account_manager_ready_cb (GObject *source_object,
1897     GAsyncResult *result,
1898     gpointer user_data)
1899 {
1900   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (user_data);
1901   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
1902   GError *error = NULL;
1903   TpConnectionPresenceType state;
1904
1905   if (!tp_account_manager_prepare_finish (account_manager, result, &error))
1906     {
1907       DEBUG ("Failed to prepare account manager: %s", error->message);
1908       g_error_free (error);
1909       goto out;
1910     }
1911
1912   state = tp_account_manager_get_most_available_presence (account_manager, NULL,
1913       NULL);
1914
1915   /* simulate a presence change so the apply button will be changed
1916    * if needed */
1917   presence_changed_cb (account_manager, state, NULL, NULL, self);
1918
1919 out:
1920   g_object_unref (self);
1921 }
1922
1923 #define WIDGET(cm, proto) \
1924   { #cm, #proto, "empathy-account-widget-"#proto".ui", \
1925     account_widget_build_##proto }
1926
1927 #ifndef HAVE_MEEGO
1928 /* Meego doesn't support registration */
1929 static void
1930 add_register_buttons (EmpathyAccountWidget *self,
1931     TpAccount *account)
1932 {
1933   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1934   const TpConnectionManagerProtocol *protocol;
1935   GtkWidget *radiobutton_register;
1936   GtkWidget *vbox = self->ui_details->widget;
1937
1938   if (!priv->creating_account)
1939     return;
1940
1941   protocol = empathy_account_settings_get_tp_protocol (priv->settings);
1942   if (protocol == NULL)
1943     return;
1944
1945   if (!tp_connection_manager_protocol_can_register (protocol))
1946     return;
1947
1948   if (account_widget_get_service (self) != NO_SERVICE)
1949     return;
1950
1951   if (priv->simple)
1952     return;
1953
1954   priv->radiobutton_reuse = gtk_radio_button_new_with_label (NULL,
1955       _("This account already exists on the server"));
1956   radiobutton_register = gtk_radio_button_new_with_label (
1957       gtk_radio_button_get_group (GTK_RADIO_BUTTON (priv->radiobutton_reuse)),
1958       _("Create a new account on the server"));
1959
1960   gtk_box_pack_start (GTK_BOX (vbox), priv->radiobutton_reuse, FALSE, FALSE, 0);
1961   gtk_box_pack_start (GTK_BOX (vbox), radiobutton_register, FALSE, FALSE, 0);
1962   gtk_box_reorder_child (GTK_BOX (vbox), priv->radiobutton_reuse, 0);
1963   gtk_box_reorder_child (GTK_BOX (vbox), radiobutton_register, 1);
1964   gtk_widget_show (priv->radiobutton_reuse);
1965   gtk_widget_show (radiobutton_register);
1966 }
1967 #endif /* HAVE_MEEGO */
1968
1969 static void
1970 remember_password_toggled_cb (GtkToggleButton *button,
1971     EmpathyAccountWidget *self)
1972 {
1973   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1974
1975   if (gtk_toggle_button_get_active (button))
1976     {
1977       gtk_widget_set_sensitive (priv->param_password_widget, TRUE);
1978     }
1979   else
1980     {
1981       gtk_widget_set_sensitive (priv->param_password_widget, FALSE);
1982       gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), "");
1983       empathy_account_settings_unset (priv->settings, "password");
1984     }
1985 }
1986
1987 static void
1988 account_settings_password_retrieved_cb (GObject *object,
1989     gpointer user_data)
1990 {
1991   EmpathyAccountWidget *self = user_data;
1992   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1993   const gchar *password = empathy_account_settings_get_string (
1994       priv->settings, "password");
1995
1996   if (password != NULL)
1997     {
1998       /* We have to do this so that when we call gtk_entry_set_text,
1999        * the ::changed callback doesn't think the user made the
2000        * change. */
2001       priv->automatic_change = TRUE;
2002       gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), password);
2003       priv->automatic_change = FALSE;
2004     }
2005
2006   gtk_toggle_button_set_active (
2007       GTK_TOGGLE_BUTTON (priv->remember_password_widget),
2008       !EMP_STR_EMPTY (password));
2009 }
2010
2011 static void
2012 do_constructed (GObject *obj)
2013 {
2014   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2015   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2016   TpAccount *account;
2017   TpStorageRestrictionFlags storage_restrictions;
2018   const gchar *display_name, *default_display_name;
2019   guint i = 0;
2020   struct {
2021     const gchar *cm_name;
2022     const gchar *protocol;
2023     const char *file;
2024     void (*func)(EmpathyAccountWidget *self, const gchar *filename);
2025   } widgets [] = {
2026     { "salut", "local-xmpp", "empathy-account-widget-local-xmpp.ui",
2027         account_widget_build_salut },
2028     WIDGET (gabble, jabber),
2029     WIDGET (butterfly, msn),
2030     WIDGET (haze, icq),
2031     WIDGET (haze, aim),
2032     WIDGET (haze, yahoo),
2033     WIDGET (haze, groupwise),
2034     WIDGET (idle, irc),
2035     WIDGET (sofiasip, sip),
2036   };
2037
2038   account = empathy_account_settings_get_account (priv->settings);
2039
2040   if (account != NULL)
2041     storage_restrictions = tp_account_get_storage_restrictions (account);
2042   else
2043     storage_restrictions = 0;
2044
2045   /* Empathy can only edit accounts without the Cannot_Set_Parameters flag */
2046   if (storage_restrictions & TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS)
2047     {
2048       DEBUG ("Account is provided by an external storage provider");
2049
2050       account_widget_build_external (self, priv->settings);
2051     }
2052   else
2053     {
2054       const gchar *protocol, *cm_name;
2055
2056       cm_name = empathy_account_settings_get_cm (priv->settings);
2057       protocol = empathy_account_settings_get_protocol (priv->settings);
2058
2059       for (i = 0 ; i < G_N_ELEMENTS (widgets); i++)
2060         {
2061           if (!tp_strdiff (widgets[i].cm_name, cm_name) &&
2062               !tp_strdiff (widgets[i].protocol, protocol))
2063             {
2064               gchar *filename;
2065
2066               filename = empathy_file_lookup (widgets[i].file,
2067                   "libempathy-gtk");
2068               widgets[i].func (self, filename);
2069               g_free (filename);
2070
2071               break;
2072             }
2073         }
2074
2075       if (i == G_N_ELEMENTS (widgets))
2076         {
2077           gchar *filename = empathy_file_lookup (
2078               "empathy-account-widget-generic.ui", "libempathy-gtk");
2079           account_widget_build_generic (self, filename);
2080           g_free (filename);
2081         }
2082     }
2083
2084   /* handle default focus */
2085   if (self->ui_details->default_focus != NULL)
2086     {
2087       GObject *default_focus_entry;
2088
2089       default_focus_entry = gtk_builder_get_object
2090         (self->ui_details->gui, self->ui_details->default_focus);
2091       g_signal_connect (default_focus_entry, "realize",
2092           G_CALLBACK (gtk_widget_grab_focus),
2093           NULL);
2094     }
2095
2096   /* remember password */
2097   if (priv->param_password_widget != NULL
2098       && priv->remember_password_widget != NULL
2099       && empathy_account_settings_supports_sasl (priv->settings))
2100     {
2101       if (priv->simple)
2102         {
2103           gtk_toggle_button_set_active (
2104               GTK_TOGGLE_BUTTON (priv->remember_password_widget), TRUE);
2105         }
2106       else
2107         {
2108           gtk_toggle_button_set_active (
2109               GTK_TOGGLE_BUTTON (priv->remember_password_widget),
2110               !EMP_STR_EMPTY (empathy_account_settings_get_string (
2111                       priv->settings, "password")));
2112
2113           /* The password might not have been retrieved from the
2114            * keyring yet. We should update the remember password
2115            * toggle button and the password entry when/if it is. */
2116           tp_g_signal_connect_object (priv->settings, "password-retrieved",
2117               G_CALLBACK (account_settings_password_retrieved_cb), self, 0);
2118         }
2119
2120       g_signal_connect (priv->remember_password_widget, "toggled",
2121           G_CALLBACK (remember_password_toggled_cb), self);
2122
2123       remember_password_toggled_cb (
2124           GTK_TOGGLE_BUTTON (priv->remember_password_widget), self);
2125     }
2126   else if (priv->remember_password_widget != NULL
2127       && !empathy_account_settings_supports_sasl (priv->settings))
2128     {
2129       gtk_widget_set_visible (priv->remember_password_widget, FALSE);
2130     }
2131
2132   /* dup and init the account-manager */
2133   priv->account_manager = tp_account_manager_dup ();
2134
2135   g_object_ref (self);
2136   tp_account_manager_prepare_async (priv->account_manager, NULL,
2137       account_manager_ready_cb, self);
2138
2139   /* handle apply and cancel button */
2140   if (!priv->simple &&
2141       !(storage_restrictions &
2142         TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS))
2143     {
2144       GtkWidget *hbox = gtk_hbox_new (TRUE, 3);
2145       GtkWidget *image;
2146
2147       /*  We can't use the stock button as its accelerator ('C') clashes with
2148        *  the Close button. */
2149       priv->cancel_button = gtk_button_new ();
2150       gtk_button_set_label (GTK_BUTTON (priv->cancel_button), _("Ca_ncel"));
2151       gtk_button_set_use_underline (GTK_BUTTON (priv->cancel_button), TRUE);
2152
2153       image = gtk_image_new_from_stock (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
2154       gtk_button_set_image (GTK_BUTTON (priv->cancel_button), image);
2155
2156       priv->apply_button = gtk_button_new ();
2157       set_apply_button (self);
2158
2159       /* We'll change this button to a "Log in" one if we are creating a new
2160        * account and are connected. */
2161       tp_g_signal_connect_object (priv->account_manager,
2162           "most-available-presence-changed",
2163           G_CALLBACK (presence_changed_cb), obj, 0);
2164
2165       gtk_box_pack_end (GTK_BOX (hbox), priv->apply_button, TRUE,
2166           TRUE, 3);
2167       gtk_box_pack_end (GTK_BOX (hbox), priv->cancel_button, TRUE,
2168           TRUE, 3);
2169
2170       gtk_box_pack_end (GTK_BOX (self->ui_details->widget), hbox, FALSE,
2171           FALSE, 3);
2172
2173       g_signal_connect (priv->cancel_button, "clicked",
2174           G_CALLBACK (account_widget_cancel_clicked_cb),
2175           self);
2176       g_signal_connect (priv->apply_button, "clicked",
2177           G_CALLBACK (account_widget_apply_clicked_cb),
2178           self);
2179       gtk_widget_show_all (hbox);
2180
2181       if (priv->creating_account)
2182         /* When creating an account, the user might have nothing to enter.
2183          * That means that no control interaction might occur,
2184          * so we update the control button sensitivity manually.
2185          */
2186         account_widget_handle_control_buttons_sensitivity (self);
2187       else
2188         account_widget_set_control_buttons_sensitivity (self, FALSE);
2189     }
2190
2191 #ifndef HAVE_MEEGO
2192   add_register_buttons (self, account);
2193 #endif /* HAVE_MEEGO */
2194
2195   /* hook up to widget destruction to unref ourselves */
2196   g_signal_connect (self->ui_details->widget, "destroy",
2197       G_CALLBACK (account_widget_destroy_cb), self);
2198
2199   if (self->ui_details->gui != NULL)
2200     {
2201       empathy_builder_unref_and_keep_widget (self->ui_details->gui,
2202           self->ui_details->widget);
2203       self->ui_details->gui = NULL;
2204     }
2205
2206   display_name = empathy_account_settings_get_display_name (priv->settings);
2207   default_display_name = empathy_account_widget_get_default_display_name (self);
2208
2209   if (tp_strdiff (display_name, default_display_name) &&
2210       !priv->creating_account)
2211     {
2212       /* The display name of the account is not the one that we'd assign by
2213        * default; assume that the user changed it manually */
2214       g_object_set (priv->settings, "display-name-overridden", TRUE, NULL);
2215     }
2216 }
2217
2218 static void
2219 do_dispose (GObject *obj)
2220 {
2221   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2222   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2223
2224   if (priv->dispose_run)
2225     return;
2226
2227   priv->dispose_run = TRUE;
2228
2229   if (priv->settings != NULL)
2230     {
2231       g_object_unref (priv->settings);
2232       priv->settings = NULL;
2233     }
2234
2235   if (priv->account_manager != NULL)
2236     {
2237       g_object_unref (priv->account_manager);
2238       priv->account_manager = NULL;
2239     }
2240
2241   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose != NULL)
2242     G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose (obj);
2243 }
2244
2245 static void
2246 do_finalize (GObject *obj)
2247 {
2248   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2249   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2250
2251   g_free (self->ui_details->default_focus);
2252   g_slice_free (EmpathyAccountWidgetUIDetails, self->ui_details);
2253
2254   g_free (priv->jid_suffix);
2255
2256   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize != NULL)
2257     G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize (obj);
2258 }
2259
2260 static void
2261 empathy_account_widget_class_init (EmpathyAccountWidgetClass *klass)
2262 {
2263   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2264   GParamSpec *param_spec;
2265
2266   oclass->get_property = do_get_property;
2267   oclass->set_property = do_set_property;
2268   oclass->constructed = do_constructed;
2269   oclass->dispose = do_dispose;
2270   oclass->finalize = do_finalize;
2271
2272   param_spec = g_param_spec_string ("protocol",
2273       "protocol", "The protocol of the account",
2274       NULL,
2275       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2276   g_object_class_install_property (oclass, PROP_PROTOCOL, param_spec);
2277
2278   param_spec = g_param_spec_object ("settings",
2279       "settings", "The settings of the account",
2280       EMPATHY_TYPE_ACCOUNT_SETTINGS,
2281       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2282   g_object_class_install_property (oclass, PROP_SETTINGS, param_spec);
2283
2284   param_spec = g_param_spec_boolean ("simple",
2285       "simple", "Whether the account widget is a simple or an advanced one",
2286       FALSE,
2287       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2288   g_object_class_install_property (oclass, PROP_SIMPLE, param_spec);
2289
2290   param_spec = g_param_spec_boolean ("creating-account",
2291       "creating-account",
2292       "TRUE if we're creating an account, FALSE if we're modifying it",
2293       FALSE,
2294       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2295   g_object_class_install_property (oclass, PROP_CREATING_ACCOUNT, param_spec);
2296
2297   param_spec = g_param_spec_boolean ("other-accounts-exist",
2298       "other-accounts-exist",
2299       "TRUE if there are any other accounts (even if this isn't yet saved)",
2300       FALSE,
2301       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2302   g_object_class_install_property (oclass, PROP_OTHER_ACCOUNTS_EXIST,
2303                   param_spec);
2304
2305   signals[HANDLE_APPLY] =
2306     g_signal_new ("handle-apply", G_TYPE_FROM_CLASS (klass),
2307         G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2308         g_cclosure_marshal_VOID__BOOLEAN,
2309         G_TYPE_NONE,
2310         1, G_TYPE_BOOLEAN);
2311
2312   /* This signal is emitted when an account has been created and enabled. */
2313   signals[ACCOUNT_CREATED] =
2314       g_signal_new ("account-created", G_TYPE_FROM_CLASS (klass),
2315           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2316           g_cclosure_marshal_VOID__OBJECT,
2317           G_TYPE_NONE,
2318           1, G_TYPE_OBJECT);
2319
2320   signals[CANCELLED] =
2321       g_signal_new ("cancelled", G_TYPE_FROM_CLASS (klass),
2322           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2323           g_cclosure_marshal_VOID__VOID,
2324           G_TYPE_NONE,
2325           0);
2326
2327   g_type_class_add_private (klass, sizeof (EmpathyAccountWidgetPriv));
2328 }
2329
2330 static void
2331 empathy_account_widget_init (EmpathyAccountWidget *self)
2332 {
2333   EmpathyAccountWidgetPriv *priv =
2334     G_TYPE_INSTANCE_GET_PRIVATE ((self), EMPATHY_TYPE_ACCOUNT_WIDGET,
2335         EmpathyAccountWidgetPriv);
2336
2337   self->priv = priv;
2338   priv->dispose_run = FALSE;
2339
2340   self->ui_details = g_slice_new0 (EmpathyAccountWidgetUIDetails);
2341 }
2342
2343 /* public methods */
2344
2345 void
2346 empathy_account_widget_discard_pending_changes
2347     (EmpathyAccountWidget *widget)
2348 {
2349   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
2350
2351   empathy_account_settings_discard_changes (priv->settings);
2352   priv->contains_pending_changes = FALSE;
2353 }
2354
2355 gboolean
2356 empathy_account_widget_contains_pending_changes (EmpathyAccountWidget *widget)
2357 {
2358   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
2359
2360   return priv->contains_pending_changes;
2361 }
2362
2363 void
2364 empathy_account_widget_handle_params (EmpathyAccountWidget *self,
2365     const gchar *first_widget,
2366     ...)
2367 {
2368   va_list args;
2369
2370   va_start (args, first_widget);
2371   account_widget_handle_params_valist (self, first_widget, args);
2372   va_end (args);
2373 }
2374
2375 GtkWidget *
2376 empathy_account_widget_get_widget (EmpathyAccountWidget *widget)
2377 {
2378   return widget->ui_details->widget;
2379 }
2380
2381 EmpathyAccountWidget *
2382 empathy_account_widget_new_for_protocol (EmpathyAccountSettings *settings,
2383     gboolean simple)
2384 {
2385   EmpathyAccountWidget *self;
2386
2387   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), NULL);
2388
2389   self = g_object_new
2390     (EMPATHY_TYPE_ACCOUNT_WIDGET,
2391         "settings", settings, "simple", simple,
2392         "creating-account",
2393         empathy_account_settings_get_account (settings) == NULL,
2394         NULL);
2395
2396   return self;
2397 }
2398
2399 gchar *
2400 empathy_account_widget_get_default_display_name (EmpathyAccountWidget *self)
2401 {
2402   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2403   const gchar *login_id;
2404   const gchar *protocol, *p;
2405   gchar *default_display_name;
2406   Service service;
2407
2408   login_id = empathy_account_settings_get_string (priv->settings, "account");
2409   protocol = empathy_account_settings_get_protocol (priv->settings);
2410   service = account_widget_get_service (self);
2411
2412   if (login_id != NULL)
2413     {
2414       /* TODO: this should be done in empathy-account-widget-irc */
2415       if (!tp_strdiff (protocol, "irc"))
2416         {
2417           EmpathyIrcNetwork *network;
2418
2419           network = empathy_irc_network_chooser_get_network (
2420               priv->irc_network_chooser);
2421           g_assert (network != NULL);
2422
2423           /* To translators: The first parameter is the login id and the
2424            * second one is the network. The resulting string will be something
2425            * like: "MyUserName on freenode".
2426            * You should reverse the order of these arguments if the
2427            * server should come before the login id in your locale.*/
2428           default_display_name = g_strdup_printf (_("%1$s on %2$s"),
2429               login_id, empathy_irc_network_get_name (network));
2430         }
2431       else if (service == FACEBOOK_SERVICE)
2432         {
2433           gchar *tmp;
2434
2435           tmp = remove_jid_suffix (self, login_id);
2436           default_display_name = g_strdup_printf ("Facebook (%s)", tmp);
2437           g_free (tmp);
2438         }
2439       else
2440         {
2441           default_display_name = g_strdup (login_id);
2442         }
2443
2444       return default_display_name;
2445     }
2446
2447   if ((p = empathy_protocol_name_to_display_name (protocol)) != NULL)
2448     protocol = p;
2449
2450   if (protocol != NULL)
2451     {
2452       /* To translators: The parameter is the protocol name. The resulting
2453        * string will be something like: "Jabber Account" */
2454       default_display_name = g_strdup_printf (_("%s Account"), protocol);
2455     }
2456   else
2457     {
2458       default_display_name = g_strdup (_("New account"));
2459     }
2460
2461   return default_display_name;
2462 }
2463
2464 /* Used by subclass to indicate that widget contains pending changes */
2465 void
2466 empathy_account_widget_changed (EmpathyAccountWidget *self)
2467 {
2468   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2469
2470   account_widget_handle_control_buttons_sensitivity (self);
2471   priv->contains_pending_changes = TRUE;
2472 }
2473
2474 void
2475 empathy_account_widget_set_account_param (EmpathyAccountWidget *self,
2476     const gchar *account)
2477 {
2478   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2479
2480   if (priv->param_account_widget == NULL)
2481     return;
2482
2483   gtk_entry_set_text (GTK_ENTRY (priv->param_account_widget), account);
2484 }
2485
2486 void
2487 empathy_account_widget_set_password_param (EmpathyAccountWidget *self,
2488     const gchar *account)
2489 {
2490   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2491
2492   if (priv->param_password_widget == NULL)
2493     return;
2494
2495   gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), account);
2496 }
2497
2498 EmpathyAccountSettings *
2499 empathy_account_widget_get_settings (EmpathyAccountWidget *self)
2500 {
2501   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2502
2503   return priv->settings;
2504 }