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