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