]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-account-widget.c
Merge branch 'gnome-3-4'
[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           return;
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), 0, 0.5);
768           g_free (str);
769
770           gtk_grid_attach (GTK_GRID (grid_settings),
771               widget, 0, row, 1, 1);
772
773           gtk_widget_show (widget);
774
775           widget = gtk_entry_new ();
776           if (strcmp (param->name, "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 (param->dbus_signature[0] == 'y' ||
790           param->dbus_signature[0] == 'n' ||
791           param->dbus_signature[0] == 'q' ||
792           param->dbus_signature[0] == 'i' ||
793           param->dbus_signature[0] == 'u' ||
794           param->dbus_signature[0] == 'x' ||
795           param->dbus_signature[0] == 't' ||
796           param->dbus_signature[0] == 'd')
797         {
798           gchar   *str = NULL;
799           gdouble  minint = 0;
800           gdouble  maxint = 0;
801           gdouble  step = 1;
802
803           switch (param->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 (param->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, param->dbus_signature);
842         }
843
844       if (widget)
845         empathy_account_widget_setup_widget (self, widget, param->name);
846
847       g_free (param_name_formatted);
848     }
849 }
850
851 static void
852 account_widget_handle_params_valist (EmpathyAccountWidget *self,
853     const gchar *first_widget,
854     va_list args)
855 {
856   GObject *object;
857   const gchar *name;
858
859   for (name = first_widget; name; name = va_arg (args, const gchar *))
860     {
861       const gchar *param_name;
862
863       param_name = va_arg (args, const gchar *);
864       object = gtk_builder_get_object (self->ui_details->gui, name);
865
866       if (!object)
867         {
868           g_warning ("Builder is missing object '%s'.", name);
869           continue;
870         }
871
872       empathy_account_widget_setup_widget (self, GTK_WIDGET (object),
873           param_name);
874     }
875 }
876
877 static void
878 account_widget_cancel_clicked_cb (GtkWidget *button,
879     EmpathyAccountWidget *self)
880 {
881   g_signal_emit (self, signals[CANCELLED], 0);
882   g_signal_emit (self, signals[CLOSE], 0, GTK_RESPONSE_CANCEL);
883 }
884
885 static void
886 account_widget_account_enabled_cb (GObject *source_object,
887     GAsyncResult *res,
888     gpointer user_data)
889 {
890   GError *error = NULL;
891   TpAccount *account = TP_ACCOUNT (source_object);
892   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (user_data);
893
894   tp_account_set_enabled_finish (account, res, &error);
895
896   if (error != NULL)
897     {
898       DEBUG ("Could not enable the account: %s", error->message);
899       g_error_free (error);
900     }
901   else
902     {
903       empathy_connect_new_account (account, self->priv->account_manager);
904     }
905
906   g_signal_emit (self, signals[CLOSE], 0, GTK_RESPONSE_APPLY);
907
908   /* unref self - part of the workaround */
909   g_object_unref (self);
910 }
911
912 static void
913 account_widget_applied_cb (GObject *source_object,
914     GAsyncResult *res,
915     gpointer user_data)
916 {
917   GError *error = NULL;
918   TpAccount *account;
919   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (source_object);
920   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (user_data);
921   gboolean reconnect_required;
922   gboolean fire_close = TRUE;
923
924   empathy_account_settings_apply_finish (settings, res, &reconnect_required,
925       &error);
926
927   if (error != NULL)
928     {
929       DEBUG ("Could not apply changes to account: %s", error->message);
930       g_error_free (error);
931       return;
932     }
933
934   account = empathy_account_settings_get_account (self->priv->settings);
935
936   if (account != NULL)
937     {
938       if (self->priv->creating_account)
939         {
940           /* By default, when an account is created, we enable it. */
941
942           /* workaround to keep self alive during async call */
943           g_object_ref (self);
944
945           tp_account_set_enabled_async (account, TRUE,
946               account_widget_account_enabled_cb, self);
947           g_signal_emit (self, signals[ACCOUNT_CREATED], 0, account);
948
949           /* Will be fired in account_widget_account_enabled_cb */
950           fire_close = FALSE;
951         }
952       else
953         {
954           /* If the account was offline, we always want to try reconnecting,
955            * to give it a chance to connect if the previous params were wrong.
956            * tp_account_reconnect_async() won't do anything if the requested
957            * presence is offline anyway. */
958           if (tp_account_get_connection_status (account, NULL) ==
959               TP_CONNECTION_STATUS_DISCONNECTED)
960             reconnect_required = TRUE;
961
962           if (reconnect_required && tp_account_is_enabled (account)
963               && tp_account_is_enabled (account))
964             {
965               /* After having applied changes to a user account, we
966                * reconnect it if needed. This is done so the new
967                * information entered by the user is validated on the server. */
968               tp_account_reconnect_async (account, NULL, NULL);
969             }
970         }
971     }
972
973   if (!self->priv->destroyed)
974     account_widget_set_control_buttons_sensitivity (self, FALSE);
975
976   self->priv->contains_pending_changes = FALSE;
977
978   if (fire_close)
979     {
980       /* announce the widget can be closed */
981       g_signal_emit (self, signals[CLOSE], 0, GTK_RESPONSE_APPLY);
982     }
983
984   /* unref the widget - part of the workaround */
985   g_object_unref (self);
986 }
987
988 static void
989 account_widget_apply_and_log_in (EmpathyAccountWidget *self)
990 {
991   gboolean display_name_overridden;
992
993   if (self->priv->radiobutton_reuse != NULL)
994     {
995       gboolean reuse = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
996             self->priv->radiobutton_reuse));
997
998       DEBUG ("Set register param: %d", !reuse);
999       empathy_account_settings_set_boolean (self->priv->settings, "register",
1000           !reuse);
1001     }
1002
1003   g_object_get (self->priv->settings,
1004       "display-name-overridden", &display_name_overridden, NULL);
1005
1006   if (self->priv->creating_account || !display_name_overridden)
1007     {
1008       gchar *display_name;
1009
1010       /* set default display name for new accounts or update if user didn't
1011        * manually override it. */
1012       display_name = empathy_account_widget_get_default_display_name (self);
1013
1014       empathy_account_settings_set_display_name_async (self->priv->settings,
1015           display_name, NULL, NULL);
1016
1017       g_free (display_name);
1018     }
1019
1020   /* workaround to keep widget alive during async call */
1021   g_object_ref (self);
1022   empathy_account_settings_apply_async (self->priv->settings,
1023       account_widget_applied_cb, self);
1024 }
1025
1026 static void
1027 account_widget_apply_clicked_cb (GtkWidget *button,
1028     EmpathyAccountWidget *self)
1029 {
1030     account_widget_apply_and_log_in (self);
1031 }
1032
1033 static void
1034 account_widget_setup_generic (EmpathyAccountWidget *self)
1035 {
1036   GtkWidget *grid_common_settings;
1037   GtkWidget *grid_advanced_settings;
1038
1039   grid_common_settings = GTK_WIDGET (gtk_builder_get_object
1040       (self->ui_details->gui, "grid_common_settings"));
1041   grid_advanced_settings = GTK_WIDGET (gtk_builder_get_object
1042       (self->ui_details->gui, "grid_advanced_settings"));
1043
1044   accounts_widget_generic_setup (self, grid_common_settings,
1045       grid_advanced_settings);
1046
1047   g_object_unref (self->ui_details->gui);
1048 }
1049
1050 static void
1051 account_widget_settings_ready_cb (EmpathyAccountSettings *settings,
1052     GParamSpec *pspec,
1053     gpointer user_data)
1054 {
1055   EmpathyAccountWidget *self = user_data;
1056
1057   if (empathy_account_settings_is_ready (self->priv->settings))
1058     account_widget_setup_generic (self);
1059 }
1060
1061 static GtkWidget *
1062 account_widget_build_generic (EmpathyAccountWidget *self,
1063     const char *filename)
1064 {
1065   GtkWidget *expander_advanced, *box;
1066
1067   self->ui_details->gui = empathy_builder_get_file (filename,
1068       "grid_common_settings", &self->priv->grid_common_settings,
1069       "vbox_generic_settings", &box,
1070       "expander_advanced_settings", &expander_advanced,
1071       NULL);
1072
1073   if (self->priv->simple)
1074     gtk_widget_hide (expander_advanced);
1075
1076   g_object_ref (self->ui_details->gui);
1077
1078   if (empathy_account_settings_is_ready (self->priv->settings))
1079     account_widget_setup_generic (self);
1080   else
1081     g_signal_connect (self->priv->settings, "notify::ready",
1082         G_CALLBACK (account_widget_settings_ready_cb), self);
1083
1084   return box;
1085 }
1086
1087 static GtkWidget *
1088 account_widget_build_salut (EmpathyAccountWidget *self,
1089     const char *filename)
1090 {
1091   GtkWidget *expander_advanced, *box;
1092
1093   self->ui_details->gui = empathy_builder_get_file (filename,
1094       "grid_common_settings", &self->priv->grid_common_settings,
1095       "vbox_salut_settings", &box,
1096       "expander_advanced_settings", &expander_advanced,
1097       NULL);
1098
1099   empathy_account_widget_handle_params (self,
1100       "entry_published", "published-name",
1101       "entry_nickname", "nickname",
1102       "entry_first_name", "first-name",
1103       "entry_last_name", "last-name",
1104       "entry_email", "email",
1105       "entry_jid", "jid",
1106       NULL);
1107
1108   if (self->priv->simple)
1109     gtk_widget_hide (expander_advanced);
1110
1111   self->ui_details->default_focus = g_strdup ("entry_first_name");
1112
1113   return box;
1114 }
1115
1116 static GtkWidget *
1117 account_widget_build_irc (EmpathyAccountWidget *self,
1118   const char *filename)
1119 {
1120   GtkWidget *box;
1121
1122   empathy_account_settings_set_regex (self->priv->settings, "account",
1123       ACCOUNT_REGEX_IRC);
1124   empathy_account_settings_set_regex (self->priv->settings, "username",
1125       USERNAME_REGEX_IRC);
1126
1127   if (self->priv->simple)
1128     {
1129       self->priv->irc_network_chooser = empathy_account_widget_irc_build_simple
1130         (self, filename, &box);
1131     }
1132   else
1133     {
1134       self->priv->irc_network_chooser = empathy_account_widget_irc_build (self,
1135           filename, &self->priv->grid_common_settings, &box);
1136     }
1137
1138   return box;
1139 }
1140
1141 static GtkWidget *
1142 account_widget_build_sip (EmpathyAccountWidget *self,
1143   const char *filename)
1144 {
1145   GtkWidget *box;
1146
1147   box = empathy_account_widget_sip_build (self, filename,
1148     &self->priv->grid_common_settings);
1149
1150   if (self->priv->simple)
1151     {
1152       self->priv->remember_password_widget = GTK_WIDGET (
1153           gtk_builder_get_object (self->ui_details->gui,
1154             "remember_password_simple"));
1155     }
1156   else
1157     {
1158       self->priv->remember_password_widget = GTK_WIDGET (
1159           gtk_builder_get_object (self->ui_details->gui, "remember_password"));
1160     }
1161
1162   return box;
1163 }
1164
1165 static GtkWidget *
1166 account_widget_build_msn (EmpathyAccountWidget *self,
1167     const char *filename)
1168 {
1169   GtkWidget *box;
1170
1171   empathy_account_settings_set_regex (self->priv->settings, "account",
1172       ACCOUNT_REGEX_MSN);
1173
1174   if (self->priv->simple)
1175     {
1176       self->ui_details->gui = empathy_builder_get_file (filename,
1177           "vbox_msn_simple", &box,
1178           NULL);
1179
1180       empathy_account_widget_handle_params (self,
1181           "entry_id_simple", "account",
1182           "entry_password_simple", "password",
1183           NULL);
1184
1185       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1186
1187       self->priv->remember_password_widget = GTK_WIDGET (
1188           gtk_builder_get_object (self->ui_details->gui,
1189             "remember_password_simple"));
1190     }
1191   else
1192     {
1193       self->ui_details->gui = empathy_builder_get_file (filename,
1194           "grid_common_msn_settings", &self->priv->grid_common_settings,
1195           "vbox_msn_settings", &box,
1196           NULL);
1197
1198       empathy_account_widget_handle_params (self,
1199           "entry_id", "account",
1200           "entry_password", "password",
1201           "entry_server", "server",
1202           "spinbutton_port", "port",
1203           NULL);
1204
1205       self->ui_details->default_focus = g_strdup ("entry_id");
1206
1207       self->priv->remember_password_widget = GTK_WIDGET (
1208           gtk_builder_get_object (self->ui_details->gui, "remember_password"));
1209     }
1210
1211   return box;
1212 }
1213
1214 static void
1215 suffix_id_widget_changed_cb (GtkWidget *entry,
1216     EmpathyAccountWidget *self)
1217 {
1218   const gchar *account;
1219
1220   g_assert (self->priv->jid_suffix != NULL);
1221
1222   account_widget_entry_changed_common (self, GTK_ENTRY (entry), FALSE);
1223
1224   account = empathy_account_settings_get_string (self->priv->settings,
1225       "account");
1226   if (!EMP_STR_EMPTY (account) &&
1227       !g_str_has_suffix (account, self->priv->jid_suffix))
1228     {
1229       gchar *tmp;
1230
1231       tmp = g_strdup_printf ("%s%s", account, self->priv->jid_suffix);
1232
1233       DEBUG ("Change account from '%s' to '%s'", account, tmp);
1234
1235       empathy_account_settings_set_string (self->priv->settings, "account",
1236           tmp);
1237       g_free (tmp);
1238     }
1239
1240   empathy_account_widget_changed (self);
1241 }
1242
1243 static gchar *
1244 remove_jid_suffix (EmpathyAccountWidget *self,
1245     const gchar *str)
1246 {
1247   g_assert (self->priv->jid_suffix != NULL);
1248
1249   if (!g_str_has_suffix (str, self->priv->jid_suffix))
1250     return g_strdup (str);
1251
1252   return g_strndup (str, strlen (str) - strlen (self->priv->jid_suffix));
1253 }
1254
1255 static void
1256 setup_id_widget_with_suffix (EmpathyAccountWidget *self,
1257     GtkWidget *widget,
1258     const gchar *suffix)
1259 {
1260   const gchar *str = NULL;
1261
1262   g_object_set_data_full (G_OBJECT (widget), "param_name",
1263       g_strdup ("account"), g_free);
1264
1265   g_assert (self->priv->jid_suffix == NULL);
1266   self->priv->jid_suffix = g_strdup (suffix);
1267
1268   str = empathy_account_settings_get_string (self->priv->settings, "account");
1269   if (str != NULL)
1270     {
1271       gchar *tmp;
1272
1273       tmp = remove_jid_suffix (self, str);
1274       gtk_entry_set_text (GTK_ENTRY (widget), tmp);
1275       g_free (tmp);
1276     }
1277
1278   self->priv->param_account_widget = widget;
1279
1280   g_signal_connect (widget, "changed",
1281       G_CALLBACK (suffix_id_widget_changed_cb), self);
1282 }
1283
1284 static Service
1285 account_widget_get_service (EmpathyAccountWidget *self)
1286 {
1287   const gchar *icon_name, *service;
1288
1289   icon_name = empathy_account_settings_get_icon_name (self->priv->settings);
1290   service = empathy_account_settings_get_service (self->priv->settings);
1291
1292   /* Previous versions of Empathy didn't set the Service property on Facebook
1293    * and gtalk accounts, so we check using the icon name as well. */
1294   if (!tp_strdiff (icon_name, "im-google-talk") ||
1295       !tp_strdiff (service, "google-talk"))
1296     return GTALK_SERVICE;
1297
1298   if (!tp_strdiff (icon_name, "im-facebook") ||
1299       !tp_strdiff (service, "facebook"))
1300     return FACEBOOK_SERVICE;
1301
1302   return NO_SERVICE;
1303 }
1304
1305 static GtkWidget *
1306 account_widget_build_jabber (EmpathyAccountWidget *self,
1307     const char *filename)
1308 {
1309   GtkWidget *spinbutton_port;
1310   GtkWidget *checkbutton_ssl;
1311   GtkWidget *label_id, *label_password;
1312   GtkWidget *label_id_create, *label_password_create;
1313   GtkWidget *label_example_fb;
1314   GtkWidget *label_example;
1315   GtkWidget *expander_advanced;
1316   GtkWidget *entry_id;
1317   GtkWidget *box;
1318   Service service;
1319
1320   service = account_widget_get_service (self);
1321
1322   empathy_account_settings_set_regex (self->priv->settings, "account",
1323       ACCOUNT_REGEX_JABBER);
1324
1325   if (self->priv->simple && service == NO_SERVICE)
1326     {
1327       /* Simple widget for XMPP */
1328       self->ui_details->gui = empathy_builder_get_file (filename,
1329           "vbox_jabber_simple", &box,
1330           "label_id_simple", &label_id,
1331           "label_id_create", &label_id_create,
1332           "label_password_simple", &label_password,
1333           "label_password_create", &label_password_create,
1334           NULL);
1335
1336       if (empathy_account_settings_get_boolean (self->priv->settings,
1337             "register"))
1338         {
1339           gtk_widget_hide (label_id);
1340           gtk_widget_hide (label_password);
1341           gtk_widget_show (label_id_create);
1342           gtk_widget_show (label_password_create);
1343         }
1344
1345       empathy_account_widget_handle_params (self,
1346           "entry_id_simple", "account",
1347           "entry_password_simple", "password",
1348           NULL);
1349
1350       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1351
1352       self->priv->remember_password_widget = GTK_WIDGET (
1353           gtk_builder_get_object (self->ui_details->gui,
1354             "remember_password_simple"));
1355     }
1356   else if (self->priv->simple && service == GTALK_SERVICE)
1357     {
1358       /* Simple widget for Google Talk */
1359       self->ui_details->gui = empathy_builder_get_file (filename,
1360           "vbox_gtalk_simple", &box,
1361           NULL);
1362
1363       empathy_account_widget_handle_params (self,
1364           "entry_id_g_simple", "account",
1365           "entry_password_g_simple", "password",
1366           NULL);
1367
1368       self->ui_details->default_focus = g_strdup ("entry_id_g_simple");
1369
1370       self->priv->remember_password_widget = GTK_WIDGET (
1371           gtk_builder_get_object (self->ui_details->gui,
1372             "remember_password_g_simple"));
1373     }
1374   else if (self->priv->simple && service == FACEBOOK_SERVICE)
1375     {
1376       /* Simple widget for Facebook */
1377       self->ui_details->gui = empathy_builder_get_file (filename,
1378           "vbox_fb_simple", &box,
1379           "entry_id_fb_simple", &entry_id,
1380           NULL);
1381
1382       empathy_account_widget_handle_params (self,
1383           "entry_password_fb_simple", "password",
1384           NULL);
1385
1386       setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
1387
1388       self->ui_details->default_focus = g_strdup ("entry_id_fb_simple");
1389
1390       self->priv->remember_password_widget = GTK_WIDGET (
1391           gtk_builder_get_object (self->ui_details->gui,
1392             "remember_password_fb_simple"));
1393     }
1394   else
1395     {
1396       ServiceInfo info = services_infos[service];
1397
1398       /* Full widget for XMPP, Google Talk and Facebook*/
1399       self->ui_details->gui = empathy_builder_get_file (filename,
1400           "grid_common_settings", &self->priv->grid_common_settings,
1401           "vbox_jabber_settings", &box,
1402           "spinbutton_port", &spinbutton_port,
1403           "checkbutton_ssl", &checkbutton_ssl,
1404           "label_username_f_example", &label_example_fb,
1405           info.label_username_example, &label_example,
1406           "expander_advanced", &expander_advanced,
1407           "entry_id", &entry_id,
1408           "label_id", &label_id,
1409           NULL);
1410
1411       empathy_account_widget_handle_params (self,
1412           "entry_password", "password",
1413           "entry_resource", "resource",
1414           "entry_server", "server",
1415           "spinbutton_port", "port",
1416           "spinbutton_priority", "priority",
1417           "checkbutton_ssl", "old-ssl",
1418           "checkbutton_ignore_ssl_errors", "ignore-ssl-errors",
1419           "checkbutton_encryption", "require-encryption",
1420           NULL);
1421
1422       if (service == FACEBOOK_SERVICE)
1423         {
1424           gtk_label_set_label (GTK_LABEL (label_id), _("Username:"));
1425
1426           /* Facebook special case the entry ID widget to hide the
1427            * "@chat.facebook.com" part */
1428           setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
1429         }
1430       else
1431         {
1432           empathy_account_widget_setup_widget (self, entry_id, "account");
1433         }
1434
1435       self->ui_details->default_focus = g_strdup ("entry_id");
1436       self->priv->spinbutton_port = spinbutton_port;
1437
1438       self->priv->remember_password_widget = GTK_WIDGET (
1439           gtk_builder_get_object (self->ui_details->gui, "remember_password"));
1440
1441       g_signal_connect (checkbutton_ssl, "toggled",
1442           G_CALLBACK (account_widget_jabber_ssl_toggled_cb),
1443           self);
1444
1445       if (service == FACEBOOK_SERVICE)
1446         {
1447           GtkContainer *parent;
1448           GList *children;
1449
1450           /* Removing the label from list of focusable widgets */
1451           parent = GTK_CONTAINER (gtk_widget_get_parent (label_example_fb));
1452           children = gtk_container_get_children (parent);
1453           children = g_list_remove (children, label_example_fb);
1454           gtk_container_set_focus_chain (parent, children);
1455           g_list_free (children);
1456         }
1457
1458       gtk_widget_show (label_example);
1459
1460       if (!info.show_advanced)
1461         gtk_widget_hide (expander_advanced);
1462     }
1463
1464   return box;
1465 }
1466
1467 static GtkWidget *
1468 account_widget_build_icq (EmpathyAccountWidget *self,
1469     const char *filename)
1470 {
1471   GtkWidget *spinbutton_port;
1472   GtkWidget *box;
1473
1474   empathy_account_settings_set_regex (self->priv->settings, "account",
1475       ACCOUNT_REGEX_ICQ);
1476
1477   if (self->priv->simple)
1478     {
1479       self->ui_details->gui = empathy_builder_get_file (filename,
1480           "vbox_icq_simple", &box,
1481           NULL);
1482
1483       empathy_account_widget_handle_params (self,
1484           "entry_uin_simple", "account",
1485           "entry_password_simple", "password",
1486           NULL);
1487
1488       self->ui_details->default_focus = g_strdup ("entry_uin_simple");
1489
1490       self->priv->remember_password_widget = GTK_WIDGET (
1491           gtk_builder_get_object (self->ui_details->gui,
1492             "remember_password_simple"));
1493     }
1494   else
1495     {
1496       self->ui_details->gui = empathy_builder_get_file (filename,
1497           "grid_common_settings", &self->priv->grid_common_settings,
1498           "vbox_icq_settings", &box,
1499           "spinbutton_port", &spinbutton_port,
1500           NULL);
1501
1502       empathy_account_widget_handle_params (self,
1503           "entry_uin", "account",
1504           "entry_password", "password",
1505           "entry_server", "server",
1506           "spinbutton_port", "port",
1507           "entry_charset", "charset",
1508           NULL);
1509
1510       self->ui_details->default_focus = g_strdup ("entry_uin");
1511
1512       self->priv->remember_password_widget = GTK_WIDGET (
1513           gtk_builder_get_object (self->ui_details->gui, "remember_password"));
1514     }
1515
1516   return box;
1517 }
1518
1519 static GtkWidget *
1520 account_widget_build_aim (EmpathyAccountWidget *self,
1521     const char *filename)
1522 {
1523   GtkWidget *spinbutton_port, *box;
1524
1525   if (self->priv->simple)
1526     {
1527       self->ui_details->gui = empathy_builder_get_file (filename,
1528           "vbox_aim_simple", &box,
1529           NULL);
1530
1531       empathy_account_widget_handle_params (self,
1532           "entry_screenname_simple", "account",
1533           "entry_password_simple", "password",
1534           NULL);
1535
1536       self->ui_details->default_focus = g_strdup ("entry_screenname_simple");
1537
1538       self->priv->remember_password_widget = GTK_WIDGET (
1539           gtk_builder_get_object (self->ui_details->gui,
1540             "remember_password_simple"));
1541     }
1542   else
1543     {
1544       self->ui_details->gui = empathy_builder_get_file (filename,
1545           "grid_common_settings", &self->priv->grid_common_settings,
1546           "vbox_aim_settings", &box,
1547           "spinbutton_port", &spinbutton_port,
1548           NULL);
1549
1550       empathy_account_widget_handle_params (self,
1551           "entry_screenname", "account",
1552           "entry_password", "password",
1553           "entry_server", "server",
1554           "spinbutton_port", "port",
1555           NULL);
1556
1557       self->ui_details->default_focus = g_strdup ("entry_screenname");
1558
1559       self->priv->remember_password_widget = GTK_WIDGET (
1560           gtk_builder_get_object (self->ui_details->gui, "remember_password"));
1561     }
1562
1563   return box;
1564 }
1565
1566 static GtkWidget *
1567 account_widget_build_yahoo (EmpathyAccountWidget *self,
1568     const char *filename)
1569 {
1570   GtkWidget *box;
1571
1572   empathy_account_settings_set_regex (self->priv->settings, "account",
1573       ACCOUNT_REGEX_YAHOO);
1574
1575   if (self->priv->simple)
1576     {
1577       self->ui_details->gui = empathy_builder_get_file (filename,
1578           "vbox_yahoo_simple", &box,
1579           NULL);
1580
1581       empathy_account_widget_handle_params (self,
1582           "entry_id_simple", "account",
1583           "entry_password_simple", "password",
1584           NULL);
1585
1586       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1587
1588       self->priv->remember_password_widget = GTK_WIDGET (
1589           gtk_builder_get_object (self->ui_details->gui,
1590             "remember_password_simple"));
1591     }
1592   else
1593     {
1594       self->ui_details->gui = empathy_builder_get_file (filename,
1595           "grid_common_settings", &self->priv->grid_common_settings,
1596           "vbox_yahoo_settings", &box,
1597           NULL);
1598
1599       empathy_account_widget_handle_params (self,
1600           "entry_id", "account",
1601           "entry_password", "password",
1602           "entry_locale", "room-list-locale",
1603           "entry_charset", "charset",
1604           "spinbutton_port", "port",
1605           "checkbutton_ignore_invites", "ignore-invites",
1606           NULL);
1607
1608       self->ui_details->default_focus = g_strdup ("entry_id");
1609
1610       self->priv->remember_password_widget = GTK_WIDGET (
1611           gtk_builder_get_object (self->ui_details->gui, "remember_password"));
1612     }
1613
1614   return box;
1615 }
1616
1617 static GtkWidget *
1618 account_widget_build_groupwise (EmpathyAccountWidget *self,
1619     const char *filename)
1620 {
1621   GtkWidget *box;
1622
1623   if (self->priv->simple)
1624     {
1625       self->ui_details->gui = empathy_builder_get_file (filename,
1626           "vbox_groupwise_simple", &box,
1627           NULL);
1628
1629       empathy_account_widget_handle_params (self,
1630           "entry_id_simple", "account",
1631           "entry_password_simple", "password",
1632           NULL);
1633
1634       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1635
1636       self->priv->remember_password_widget = GTK_WIDGET (
1637           gtk_builder_get_object (self->ui_details->gui,
1638             "remember_password_simple"));
1639     }
1640   else
1641     {
1642       self->ui_details->gui = empathy_builder_get_file (filename,
1643           "grid_common_groupwise_settings", &self->priv->grid_common_settings,
1644           "vbox_groupwise_settings", &box,
1645           NULL);
1646
1647       empathy_account_widget_handle_params (self,
1648           "entry_id", "account",
1649           "entry_password", "password",
1650           "entry_server", "server",
1651           "spinbutton_port", "port",
1652           NULL);
1653
1654       self->ui_details->default_focus = g_strdup ("entry_id");
1655
1656       self->priv->remember_password_widget = GTK_WIDGET (
1657           gtk_builder_get_object (self->ui_details->gui, "remember_password"));
1658     }
1659
1660   return box;
1661 }
1662
1663 void
1664 empathy_account_widget_set_other_accounts_exist (EmpathyAccountWidget *self,
1665     gboolean others_exist)
1666 {
1667   self->priv->other_accounts_exist = others_exist;
1668
1669   if (self->priv->creating_account)
1670     account_widget_handle_control_buttons_sensitivity (self);
1671 }
1672
1673 static void
1674 do_set_property (GObject *object,
1675     guint prop_id,
1676     const GValue *value,
1677     GParamSpec *pspec)
1678 {
1679   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (object);
1680
1681   switch (prop_id)
1682     {
1683     case PROP_SETTINGS:
1684       self->priv->settings = g_value_dup_object (value);
1685       break;
1686     case PROP_SIMPLE:
1687       self->priv->simple = g_value_get_boolean (value);
1688       break;
1689     case PROP_CREATING_ACCOUNT:
1690       self->priv->creating_account = g_value_get_boolean (value);
1691       break;
1692     case PROP_OTHER_ACCOUNTS_EXIST:
1693       empathy_account_widget_set_other_accounts_exist (
1694           EMPATHY_ACCOUNT_WIDGET (object), g_value_get_boolean (value));
1695       break;
1696     default:
1697       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1698     }
1699 }
1700
1701 static void
1702 do_get_property (GObject *object,
1703     guint prop_id,
1704     GValue *value,
1705     GParamSpec *pspec)
1706 {
1707   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (object);
1708
1709   switch (prop_id)
1710     {
1711     case PROP_PROTOCOL:
1712       g_value_set_string (value,
1713         empathy_account_settings_get_protocol (self->priv->settings));
1714       break;
1715     case PROP_SETTINGS:
1716       g_value_set_object (value, self->priv->settings);
1717       break;
1718     case PROP_SIMPLE:
1719       g_value_set_boolean (value, self->priv->simple);
1720       break;
1721     case PROP_CREATING_ACCOUNT:
1722       g_value_set_boolean (value, self->priv->creating_account);
1723       break;
1724     case PROP_OTHER_ACCOUNTS_EXIST:
1725       g_value_set_boolean (value, self->priv->other_accounts_exist);
1726       break;
1727     default:
1728       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1729     }
1730 }
1731
1732 static void
1733 set_apply_button (EmpathyAccountWidget *self)
1734 {
1735   GtkWidget *image;
1736
1737   /* We can't use the stock button as its accelerator ('A') clashes with the
1738    * Add button. */
1739   gtk_button_set_use_stock (GTK_BUTTON (self->priv->apply_button), FALSE);
1740
1741   gtk_button_set_label (GTK_BUTTON (self->priv->apply_button), _("A_pply"));
1742   gtk_button_set_use_underline (GTK_BUTTON (self->priv->apply_button), TRUE);
1743
1744   image = gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
1745   gtk_button_set_image (GTK_BUTTON (self->priv->apply_button), image);
1746 }
1747
1748 static void
1749 presence_changed_cb (TpAccountManager *manager,
1750     TpConnectionPresenceType state,
1751     const gchar *status,
1752     const gchar *message,
1753     EmpathyAccountWidget *self)
1754 {
1755   if (self->priv->destroyed)
1756     return;
1757
1758   if (self->priv->apply_button == NULL)
1759     /* This button doesn't exist in 'simple' mode */
1760     return;
1761
1762   if (state > TP_CONNECTION_PRESENCE_TYPE_OFFLINE &&
1763       self->priv->creating_account)
1764     {
1765       /* We are online and creating a new account, display a Login button */
1766       GtkWidget *image;
1767
1768       gtk_button_set_use_stock (GTK_BUTTON (self->priv->apply_button), FALSE);
1769       gtk_button_set_label (GTK_BUTTON (self->priv->apply_button),
1770           _("L_og in"));
1771
1772       image = gtk_image_new_from_stock (GTK_STOCK_CONNECT,
1773           GTK_ICON_SIZE_BUTTON);
1774       gtk_button_set_image (GTK_BUTTON (self->priv->apply_button), image);
1775     }
1776   else
1777     {
1778       /* We are offline or modifying an existing account, display
1779        * a Save button */
1780       set_apply_button (self);
1781     }
1782 }
1783
1784 static void
1785 account_manager_ready_cb (GObject *source_object,
1786     GAsyncResult *result,
1787     gpointer user_data)
1788 {
1789   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (user_data);
1790   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
1791   GError *error = NULL;
1792   TpConnectionPresenceType state;
1793
1794   if (!tp_proxy_prepare_finish (account_manager, result, &error))
1795     {
1796       DEBUG ("Failed to prepare account manager: %s", error->message);
1797       g_error_free (error);
1798       goto out;
1799     }
1800
1801   state = tp_account_manager_get_most_available_presence (account_manager, NULL,
1802       NULL);
1803
1804   /* simulate a presence change so the apply button will be changed
1805    * if needed */
1806   presence_changed_cb (account_manager, state, NULL, NULL, self);
1807
1808 out:
1809   g_object_unref (self);
1810 }
1811
1812 #define WIDGET(cm, proto) \
1813   { #cm, #proto, "empathy-account-widget-"#proto".ui", \
1814     account_widget_build_##proto }
1815
1816 static void
1817 add_register_buttons (EmpathyAccountWidget *self,
1818     TpAccount *account)
1819 {
1820   const TpConnectionManagerProtocol *protocol;
1821   GtkWidget *radiobutton_register;
1822
1823   if (!self->priv->creating_account)
1824     return;
1825
1826   protocol = empathy_account_settings_get_tp_protocol (self->priv->settings);
1827   if (protocol == NULL)
1828     return;
1829
1830   if (!tp_connection_manager_protocol_can_register (protocol))
1831     return;
1832
1833   if (account_widget_get_service (self) != NO_SERVICE)
1834     return;
1835
1836   if (self->priv->simple)
1837     return;
1838
1839   self->priv->radiobutton_reuse = gtk_radio_button_new_with_label (NULL,
1840       _("This account already exists on the server"));
1841   radiobutton_register = gtk_radio_button_new_with_label (
1842       gtk_radio_button_get_group (
1843         GTK_RADIO_BUTTON (self->priv->radiobutton_reuse)),
1844       _("Create a new account on the server"));
1845
1846   gtk_box_pack_start (GTK_BOX (self), self->priv->radiobutton_reuse, FALSE,
1847       FALSE, 0);
1848   gtk_box_pack_start (GTK_BOX (self), radiobutton_register, FALSE, FALSE, 0);
1849   gtk_box_reorder_child (GTK_BOX (self), self->priv->radiobutton_reuse, 0);
1850   gtk_box_reorder_child (GTK_BOX (self), radiobutton_register, 1);
1851   gtk_widget_show (self->priv->radiobutton_reuse);
1852   gtk_widget_show (radiobutton_register);
1853 }
1854
1855 static void
1856 remember_password_toggled_cb (GtkToggleButton *button,
1857     EmpathyAccountWidget *self)
1858 {
1859   if (gtk_toggle_button_get_active (button))
1860     {
1861       gtk_widget_set_sensitive (self->priv->param_password_widget, TRUE);
1862     }
1863   else
1864     {
1865       gtk_widget_set_sensitive (self->priv->param_password_widget, FALSE);
1866       gtk_entry_set_text (GTK_ENTRY (self->priv->param_password_widget), "");
1867       empathy_account_settings_unset (self->priv->settings, "password");
1868     }
1869 }
1870
1871 static void
1872 account_settings_password_retrieved_cb (GObject *object,
1873     gpointer user_data)
1874 {
1875   EmpathyAccountWidget *self = user_data;
1876   const gchar *password = empathy_account_settings_get_string (
1877       self->priv->settings, "password");
1878
1879   if (password != NULL)
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. */
1884       self->priv->automatic_change = TRUE;
1885       gtk_entry_set_text (GTK_ENTRY (self->priv->param_password_widget),
1886           password);
1887       self->priv->automatic_change = FALSE;
1888     }
1889
1890   gtk_toggle_button_set_active (
1891       GTK_TOGGLE_BUTTON (self->priv->remember_password_widget),
1892       !EMP_STR_EMPTY (password));
1893 }
1894
1895 static void
1896 do_constructed (GObject *obj)
1897 {
1898   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
1899   TpAccount *account;
1900   const gchar *display_name, *default_display_name;
1901   guint i = 0;
1902   struct {
1903     const gchar *cm_name;
1904     const gchar *protocol;
1905     const char *file;
1906     GtkWidget * (*func)(EmpathyAccountWidget *self, const gchar *filename);
1907   } widgets [] = {
1908     { "salut", "local-xmpp", "empathy-account-widget-local-xmpp.ui",
1909         account_widget_build_salut },
1910     WIDGET (gabble, jabber),
1911     WIDGET (haze, msn),
1912     WIDGET (haze, icq),
1913     WIDGET (haze, aim),
1914     WIDGET (haze, yahoo),
1915     WIDGET (haze, groupwise),
1916     WIDGET (idle, irc),
1917     WIDGET (sofiasip, sip),
1918   };
1919   const gchar *protocol, *cm_name;
1920   GtkWidget *box;
1921
1922   account = empathy_account_settings_get_account (self->priv->settings);
1923
1924   cm_name = empathy_account_settings_get_cm (self->priv->settings);
1925   protocol = empathy_account_settings_get_protocol (self->priv->settings);
1926
1927   for (i = 0 ; i < G_N_ELEMENTS (widgets); i++)
1928     {
1929       if (!tp_strdiff (widgets[i].cm_name, cm_name) &&
1930           !tp_strdiff (widgets[i].protocol, protocol))
1931         {
1932           gchar *filename;
1933
1934           filename = empathy_file_lookup (widgets[i].file,
1935               "libempathy-gtk");
1936           box = widgets[i].func (self, filename);
1937           g_free (filename);
1938
1939           break;
1940         }
1941     }
1942
1943   if (i == G_N_ELEMENTS (widgets))
1944     {
1945       gchar *filename = empathy_file_lookup (
1946           "empathy-account-widget-generic.ui", "libempathy-gtk");
1947       box = account_widget_build_generic (self, filename);
1948       g_free (filename);
1949     }
1950
1951   gtk_container_add (GTK_CONTAINER (self), box);
1952
1953   /* handle default focus */
1954   if (self->ui_details->default_focus != NULL)
1955     {
1956       GObject *default_focus_entry;
1957
1958       default_focus_entry = gtk_builder_get_object
1959         (self->ui_details->gui, self->ui_details->default_focus);
1960       g_signal_connect (default_focus_entry, "realize",
1961           G_CALLBACK (gtk_widget_grab_focus),
1962           NULL);
1963     }
1964
1965   /* remember password */
1966   if (self->priv->param_password_widget != NULL
1967       && self->priv->remember_password_widget != NULL
1968       && empathy_account_settings_supports_sasl (self->priv->settings))
1969     {
1970       if (self->priv->simple)
1971         {
1972           gtk_toggle_button_set_active (
1973               GTK_TOGGLE_BUTTON (self->priv->remember_password_widget), TRUE);
1974         }
1975       else
1976         {
1977           gtk_toggle_button_set_active (
1978               GTK_TOGGLE_BUTTON (self->priv->remember_password_widget),
1979               !EMP_STR_EMPTY (empathy_account_settings_get_string (
1980                       self->priv->settings, "password")));
1981
1982           /* The password might not have been retrieved from the
1983            * keyring yet. We should update the remember password
1984            * toggle button and the password entry when/if it is. */
1985           tp_g_signal_connect_object (self->priv->settings,
1986               "password-retrieved",
1987               G_CALLBACK (account_settings_password_retrieved_cb), self, 0);
1988         }
1989
1990       g_signal_connect (self->priv->remember_password_widget, "toggled",
1991           G_CALLBACK (remember_password_toggled_cb), self);
1992
1993       remember_password_toggled_cb (
1994           GTK_TOGGLE_BUTTON (self->priv->remember_password_widget), self);
1995     }
1996   else if (self->priv->remember_password_widget != NULL
1997       && !empathy_account_settings_supports_sasl (self->priv->settings))
1998     {
1999       gtk_widget_set_visible (self->priv->remember_password_widget, FALSE);
2000     }
2001
2002   /* dup and init the account-manager */
2003   self->priv->account_manager = tp_account_manager_dup ();
2004
2005   g_object_ref (self);
2006   tp_proxy_prepare_async (self->priv->account_manager, NULL,
2007       account_manager_ready_cb, self);
2008
2009   /* handle apply and cancel button */
2010   self->priv->hbox_buttons = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
2011
2012   gtk_box_set_homogeneous (GTK_BOX (self->priv->hbox_buttons), TRUE);
2013
2014   self->priv->cancel_button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
2015
2016   self->priv->apply_button = gtk_button_new ();
2017   set_apply_button (self);
2018
2019   /* We'll change this button to a "Log in" one if we are creating a new
2020    * account and are connected. */
2021   tp_g_signal_connect_object (self->priv->account_manager,
2022       "most-available-presence-changed",
2023       G_CALLBACK (presence_changed_cb), obj, 0);
2024
2025   gtk_box_pack_end (GTK_BOX (self->priv->hbox_buttons),
2026       self->priv->apply_button, TRUE, TRUE, 3);
2027   gtk_box_pack_end (GTK_BOX (self->priv->hbox_buttons),
2028       self->priv->cancel_button, TRUE, TRUE, 3);
2029
2030   gtk_box_pack_end (GTK_BOX (self), self->priv->hbox_buttons, FALSE,
2031       FALSE, 3);
2032
2033   g_signal_connect (self->priv->cancel_button, "clicked",
2034       G_CALLBACK (account_widget_cancel_clicked_cb),
2035       self);
2036   g_signal_connect (self->priv->apply_button, "clicked",
2037       G_CALLBACK (account_widget_apply_clicked_cb),
2038       self);
2039   gtk_widget_show_all (self->priv->hbox_buttons);
2040
2041   if (self->priv->creating_account)
2042     /* When creating an account, the user might have nothing to enter.
2043      * That means that no control interaction might occur,
2044      * so we update the control button sensitivity manually.
2045      */
2046     account_widget_handle_control_buttons_sensitivity (self);
2047   else
2048     account_widget_set_control_buttons_sensitivity (self, FALSE);
2049
2050   add_register_buttons (self, account);
2051
2052   g_clear_object (&self->ui_details->gui);
2053
2054   display_name = empathy_account_settings_get_display_name (
2055       self->priv->settings);
2056   default_display_name = empathy_account_widget_get_default_display_name (self);
2057
2058   if (tp_strdiff (display_name, default_display_name) &&
2059       !self->priv->creating_account)
2060     {
2061       /* The display name of the account is not the one that we'd assign by
2062        * default; assume that the user changed it manually */
2063       g_object_set (self->priv->settings, "display-name-overridden", TRUE,
2064           NULL);
2065     }
2066 }
2067
2068 static void
2069 do_dispose (GObject *obj)
2070 {
2071   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2072
2073   g_clear_object (&self->priv->settings);
2074   g_clear_object (&self->priv->account_manager);
2075
2076   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose != NULL)
2077     G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose (obj);
2078 }
2079
2080 static void
2081 do_finalize (GObject *obj)
2082 {
2083   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2084
2085   g_free (self->ui_details->default_focus);
2086   g_slice_free (EmpathyAccountWidgetUIDetails, self->ui_details);
2087
2088   g_free (self->priv->jid_suffix);
2089
2090   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize != NULL)
2091     G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize (obj);
2092 }
2093
2094 static void
2095 empathy_account_widget_class_init (EmpathyAccountWidgetClass *klass)
2096 {
2097   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2098   GParamSpec *param_spec;
2099
2100   oclass->get_property = do_get_property;
2101   oclass->set_property = do_set_property;
2102   oclass->constructed = do_constructed;
2103   oclass->dispose = do_dispose;
2104   oclass->finalize = do_finalize;
2105
2106   param_spec = g_param_spec_string ("protocol",
2107       "protocol", "The protocol of the account",
2108       NULL,
2109       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2110   g_object_class_install_property (oclass, PROP_PROTOCOL, param_spec);
2111
2112   param_spec = g_param_spec_object ("settings",
2113       "settings", "The settings of the account",
2114       EMPATHY_TYPE_ACCOUNT_SETTINGS,
2115       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2116   g_object_class_install_property (oclass, PROP_SETTINGS, param_spec);
2117
2118   param_spec = g_param_spec_boolean ("simple",
2119       "simple", "Whether the account widget is a simple or an advanced one",
2120       FALSE,
2121       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2122   g_object_class_install_property (oclass, PROP_SIMPLE, param_spec);
2123
2124   param_spec = g_param_spec_boolean ("creating-account",
2125       "creating-account",
2126       "TRUE if we're creating an account, FALSE if we're modifying it",
2127       FALSE,
2128       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2129   g_object_class_install_property (oclass, PROP_CREATING_ACCOUNT, param_spec);
2130
2131   param_spec = g_param_spec_boolean ("other-accounts-exist",
2132       "other-accounts-exist",
2133       "TRUE if there are any other accounts (even if this isn't yet saved)",
2134       FALSE,
2135       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2136   g_object_class_install_property (oclass, PROP_OTHER_ACCOUNTS_EXIST,
2137                   param_spec);
2138
2139   signals[HANDLE_APPLY] =
2140     g_signal_new ("handle-apply", G_TYPE_FROM_CLASS (klass),
2141         G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2142         g_cclosure_marshal_generic,
2143         G_TYPE_NONE,
2144         1, G_TYPE_BOOLEAN);
2145
2146   /* This signal is emitted when an account has been created and enabled. */
2147   signals[ACCOUNT_CREATED] =
2148       g_signal_new ("account-created", G_TYPE_FROM_CLASS (klass),
2149           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2150           g_cclosure_marshal_generic,
2151           G_TYPE_NONE,
2152           1, G_TYPE_OBJECT);
2153
2154   signals[CANCELLED] =
2155       g_signal_new ("cancelled", G_TYPE_FROM_CLASS (klass),
2156           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2157           g_cclosure_marshal_generic,
2158           G_TYPE_NONE,
2159           0);
2160
2161   signals[CLOSE] =
2162     g_signal_new ("close", G_TYPE_FROM_CLASS (klass),
2163         G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2164         g_cclosure_marshal_VOID__INT,
2165         G_TYPE_NONE,
2166         1, G_TYPE_INT);
2167
2168   g_type_class_add_private (klass, sizeof (EmpathyAccountWidgetPriv));
2169 }
2170
2171 static void
2172 empathy_account_widget_init (EmpathyAccountWidget *self)
2173 {
2174   self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), EMPATHY_TYPE_ACCOUNT_WIDGET,
2175         EmpathyAccountWidgetPriv);
2176
2177   self->ui_details = g_slice_new0 (EmpathyAccountWidgetUIDetails);
2178 }
2179
2180 /* public methods */
2181
2182 void
2183 empathy_account_widget_discard_pending_changes (EmpathyAccountWidget *self)
2184 {
2185   empathy_account_settings_discard_changes (self->priv->settings);
2186   self->priv->contains_pending_changes = FALSE;
2187 }
2188
2189 gboolean
2190 empathy_account_widget_contains_pending_changes (EmpathyAccountWidget *self)
2191 {
2192   return self->priv->contains_pending_changes;
2193 }
2194
2195 void
2196 empathy_account_widget_handle_params (EmpathyAccountWidget *self,
2197     const gchar *first_widget,
2198     ...)
2199 {
2200   va_list args;
2201
2202   va_start (args, first_widget);
2203   account_widget_handle_params_valist (self, first_widget, args);
2204   va_end (args);
2205 }
2206
2207 EmpathyAccountWidget *
2208 empathy_account_widget_new_for_protocol (EmpathyAccountSettings *settings,
2209     gboolean simple)
2210 {
2211   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), NULL);
2212
2213   return g_object_new (EMPATHY_TYPE_ACCOUNT_WIDGET,
2214         "orientation", GTK_ORIENTATION_VERTICAL,
2215         "settings", settings,
2216         "simple", simple,
2217         "creating-account",
2218           empathy_account_settings_get_account (settings) == NULL,
2219         NULL);
2220 }
2221
2222 gchar *
2223 empathy_account_widget_get_default_display_name (EmpathyAccountWidget *self)
2224 {
2225   const gchar *login_id;
2226   const gchar *protocol, *p;
2227   gchar *default_display_name;
2228   Service service;
2229
2230   login_id = empathy_account_settings_get_string (self->priv->settings,
2231       "account");
2232   protocol = empathy_account_settings_get_protocol (self->priv->settings);
2233   service = account_widget_get_service (self);
2234
2235   if (login_id != NULL)
2236     {
2237       /* TODO: this should be done in empathy-account-widget-irc */
2238       if (!tp_strdiff (protocol, "irc"))
2239         {
2240           EmpathyIrcNetwork *network;
2241
2242           network = empathy_irc_network_chooser_get_network (
2243               self->priv->irc_network_chooser);
2244           g_assert (network != NULL);
2245
2246           /* To translators: The first parameter is the login id and the
2247            * second one is the network. The resulting string will be something
2248            * like: "MyUserName on freenode".
2249            * You should reverse the order of these arguments if the
2250            * server should come before the login id in your locale.*/
2251           default_display_name = g_strdup_printf (_("%1$s on %2$s"),
2252               login_id, empathy_irc_network_get_name (network));
2253         }
2254       else if (service == FACEBOOK_SERVICE && self->priv->jid_suffix != NULL)
2255         {
2256           gchar *tmp;
2257
2258           tmp = remove_jid_suffix (self, login_id);
2259           default_display_name = g_strdup_printf ("Facebook (%s)", tmp);
2260           g_free (tmp);
2261         }
2262       else
2263         {
2264           default_display_name = g_strdup (login_id);
2265         }
2266
2267       return default_display_name;
2268     }
2269
2270   if ((p = empathy_protocol_name_to_display_name (protocol)) != NULL)
2271     protocol = p;
2272
2273   if (protocol != NULL)
2274     {
2275       /* To translators: The parameter is the protocol name. The resulting
2276        * string will be something like: "Jabber Account" */
2277       default_display_name = g_strdup_printf (_("%s Account"), protocol);
2278     }
2279   else
2280     {
2281       default_display_name = g_strdup (_("New account"));
2282     }
2283
2284   return default_display_name;
2285 }
2286
2287 /* Used by subclass to indicate that widget contains pending changes */
2288 void
2289 empathy_account_widget_changed (EmpathyAccountWidget *self)
2290 {
2291   account_widget_handle_control_buttons_sensitivity (self);
2292   self->priv->contains_pending_changes = TRUE;
2293 }
2294
2295 void
2296 empathy_account_widget_set_account_param (EmpathyAccountWidget *self,
2297     const gchar *account)
2298 {
2299   if (self->priv->param_account_widget == NULL)
2300     return;
2301
2302   gtk_entry_set_text (GTK_ENTRY (self->priv->param_account_widget), account);
2303 }
2304
2305 void
2306 empathy_account_widget_set_password_param (EmpathyAccountWidget *self,
2307     const gchar *account)
2308 {
2309   if (self->priv->param_password_widget == NULL)
2310     return;
2311
2312   gtk_entry_set_text (GTK_ENTRY (self->priv->param_password_widget), account);
2313 }
2314
2315 EmpathyAccountSettings *
2316 empathy_account_widget_get_settings (EmpathyAccountWidget *self)
2317 {
2318   return self->priv->settings;
2319 }
2320
2321 void
2322 empathy_account_widget_hide_buttons (EmpathyAccountWidget *self)
2323 {
2324   gtk_widget_hide (self->priv->hbox_buttons);
2325 }