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