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