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