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