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